--- /dev/null
+
+CODE_SIGN_ENTITLEMENTS[sdk=embedded*] = dyld3/closured/closured_entitlements.plist
PRODUCT_NAME[sdk=macosx*] = dyld
INSTALL_PATH = /usr/lib
+
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1
-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
+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 -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured
+LIBSYSTEM_LIBS[sdk=embedded*] = -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 -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured -Wl,-upward-lcompiler_rt
+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 -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured
INSTALL_PATH = /usr/lib/system
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1
+
-//:configuration = Release
-LOCAL_YES = local
-//:configuration = Release
-LOCAL = $(LOCAL_$(RC_PURPLE))
-
-//:configuration = Release
-INSTALL_PATH = $(INSTALL_LOCATION)/usr/$(LOCAL)/bin
-
-// don't iOS tool
-MY_RELEASE_CODE_SIGN_IDENTITY_YES =
-MY_RELEASE_CODE_SIGN_IDENTITY_ = -
-CODE_SIGN_IDENTITY = $(MY_RELEASE_CODE_SIGN_IDENTITY_$(RC_PURPLE))
-
-
-CODE_SIGN_ENTITLEMENTS = launch-cache/update_dyld_shared_cache_entitlements.plist
+CODE_SIGN_ENTITLEMENTS = dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist
--- /dev/null
+
+#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
+
--- /dev/null
+.Dd 3/1/17
+.Dt closured 1
+.Os Darwin
+.Sh NAME
+.Nm closured
+.Nd Daemon for building dyld closures.
+.Sh SYNOPSIS
+.Nm closured is a launchd managed daemon.
+.Sh DESCRIPTION
+.Nm closured is a launchd managed daemon.
-.TH DYLD 1 "December 14, 2009" "Apple Inc."
+.TH DYLD 1 "June 1, 2017" "Apple Inc."
.SH NAME
-dyld \- the dynamic link editor
+dyld \- the dynamic linker
.SH SYNOPSIS
DYLD_FRAMEWORK_PATH
.br
.br
DYLD_PRINT_LIBRARIES
.br
-DYLD_PRINT_LIBRARIES_POST_LAUNCH
-.br
DYLD_BIND_AT_LAUNCH
.br
DYLD_DISABLE_DOFS
.br
DYLD_SHARED_CACHE_DONT_VALIDATE
.SH DESCRIPTION
-The dynamic linker uses the following environment variables.
-They affect any program that uses the dynamic linker.
+The dynamic linker checks the following environment variables during the launch
+of each process.
+.br
+.br
+Note: If System Integrity Protection is enabled, these environment variables are ignored
+when executing binaries protected by System Integrity Protection.
.TP
.B DYLD_FRAMEWORK_PATH
This is a colon separated list of directories that contain frameworks.
.SM DYLD_LIBRARY_PATH
is getting what you want.
.TP
-.B DYLD_PRINT_LIBRARIES_POST_LAUNCH
-This does the same as
-.SM DYLD_PRINT_LIBRARIES
-but the printing starts after the program gets to its entry point.
-.TP
.B DYLD_BIND_AT_LAUNCH
When this is set, the dynamic linker binds all undefined symbols
the program needs at launch time. This includes function symbols that can are normally
At runtime dyld sets it run path to be the anchor point, then each dylib is found relative
to the anchor point.
.SH "SEE ALSO"
-libtool(1), ld(1), otool(1)
+dyldinfo(1), ld(1), otool(1)
-.Dd June 23, 2016
+.Dd June 1, 2017
.Dt update_dyld_shared_cache 1
.Os Darwin
.Sh NAME
.Op Fl force
.Op Fl debug
.Op Fl universal_boot
-.Op Fl verify
.Sh DESCRIPTION
.Nm update_dyld_shared_cache
ensures that dyld's shared cache is up-to-date. This tool is normally
.Pp
Note that the new cache does not take effect until the OS is rebooted.
.Pp
-If a safe-boot is
-done (booting with shift key held down) the cache is deleted.
-.Pp
The dyld shared cache
is mapped by dyld into a process at launch time. Later, when loading
any mach-o image, dyld will first check if is in the share cache, and if
launch time.
.Pp
.Nm update_dyld_shared_cache
-scans the directory /var/db/dyld/shared_region_roots for text files containing paths to
-mach-o executables. The full dependencies of all dylibs required by those executables is
-used to determine which libraries are commonly used and should be placed in the
-shared cache. If one of the text files contains a path to a dylib, that dylib and its
-dependents will be forced into the cache.
+scans the directory /System/Library/Receipts/ for .bom files which list all files
+installed. From that info it creates the set of OS dylibs to build into the dyld cache.
.Pp
.Nm update_dyld_shared_cache
builds a separate cache file for each architecture. The cache files and a readable text
This option prints out additional information about the work being done.
.It Fl universal_boot
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.
.El
-.Sh FILES
-.Tp
-/var/db/dyld/shared_region_roots
-directory of text files with paths to mach-o images used to determine what should be in shared cache.
.Sh SEE ALSO
.Xr dyld 1
isa = PBXAggregateTarget;
buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */;
buildPhases = (
+ F94182D61E60E74E00D8EF25 /* pre-platform builds */,
);
dependencies = (
- 37A0AD1B1C16004600731E50 /* PBXTargetDependency */,
- 37A0AD131C16003600731E50 /* PBXTargetDependency */,
- 37A0AD151C16003600731E50 /* PBXTargetDependency */,
- 37A0AD171C16003600731E50 /* PBXTargetDependency */,
- 37A0AD191C16003600731E50 /* PBXTargetDependency */,
- 37A0AD111C16003600731E50 /* PBXTargetDependency */,
+ D8668AD01ECE335F005E7D31 /* PBXTargetDependency */,
+ F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */,
+ F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */,
+ F94182DC1E60F16900D8EF25 /* PBXTargetDependency */,
);
name = update_dyld_shared_cache;
productName = update_dyld_shared_cache;
isa = PBXAggregateTarget;
buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
buildPhases = (
- F908135111D3ED9000626CC1 /* usr|include|mach-o */,
- F908137011D3FB5000626CC1 /* usr|include */,
F9C69EFC14EC8AB8009CAE2E /* usr|local|include */,
- F908137111D3FB5000626CC1 /* usr|local|include|mach-o */,
F908137211D3FB5000626CC1 /* usr|share|man|man1 */,
F908137311D3FB5000626CC1 /* usr|share|man|man3 */,
- F96D19701D7F62C3007AF3CE /* Install dyld_priv.h */,
);
dependencies = (
F9B4D78012AD9736000605A6 /* PBXTargetDependency */,
/* 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 */; };
+ 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+ 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; };
+ 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ 37554F401E3F167A00407388 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+ 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+ 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+ 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+ 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+ 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+ 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+ 37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+ 37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+ 37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+ 37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ 37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ 37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ 37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ 37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.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 */; };
- F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; };
- F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F93937400A94FC4700070A07 /* dyld_cache_format.h */; };
- F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; };
+ 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+ 37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */; };
+ 37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ 37908A311E3EB585009613FA /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ 37908A321E3ED667009613FA /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+ 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+ 37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; };
+ 37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; };
+ F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; };
F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; };
F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; };
F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; };
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 */; };
+ F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */; };
F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
+ F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
+ F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */; };
+ F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
+ F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F94942B31E6796D70019AE08 /* closured.1 in install man page */ = {isa = PBXBuildFile; fileRef = F94942B21E6796D40019AE08 /* closured.1 */; };
+ F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
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, ); }; };
+ F960A78A1E40569400840176 /* dyld-interposing.h in Headers */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */ = {isa = PBXBuildFile; fileRef = F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+ F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+ F96354361DCD74A400895049 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F96354381DCD74A400895049 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+ F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+ F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */; };
+ F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+ F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A51D9363D6007AF3CE /* APIs.cpp */; };
+ F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A61D9363D6007AF3CE /* AllImages.cpp */; };
+ F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */; };
+ F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */; };
+ F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C61A01D9CA6B800A84CD7 /* Logging.cpp */; };
+ F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
+ F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+ F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; };
+ F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+ F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+ F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F988F0BA1E2FDF5B003AED79 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
+ F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ F99006DD1E411BA70013456D /* dyld_images.h in Headers */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F99006DE1E411BBC0013456D /* dyld.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; settings = {ATTRIBUTES = (Private, ); }; };
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 */; };
F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */; };
+ F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; };
F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; };
+ F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+ F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+ F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+ F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
+ F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+ F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; };
+ F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */; };
+ F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; };
+ F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; };
F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */; };
+ F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
+ F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9CE30791208F1B50098B590 /* dsc_extractor.h */; };
F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; };
F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; };
+ F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+ F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+ F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D862441DC9759C000A199A /* dyld_closure_util.cpp */; };
+ F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */; };
+ F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAA1E28787900A753DC /* closured.cpp */; };
+ F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+ F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+ F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+ F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+ F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+ F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+ F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */; };
F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC70630A7F100DF4E74 /* dyld.cpp */; };
F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC90630A7F100DF4E74 /* dyldAPIs.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 */; };
+ F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
remoteInfo = update_dyld_shared_cache;
};
- 37A0AD101C16003600731E50 /* PBXContainerItemProxy */ = {
+ D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F93937310A94FAF700070A07;
- remoteInfo = update_dyld_shared_cache_tool;
+ remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
+ remoteInfo = dyld_closure_util;
};
- 37A0AD121C16003600731E50 /* PBXContainerItemProxy */ = {
+ F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
- remoteInfo = libdsc;
+ remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+ remoteInfo = libdyld.dylib;
};
- 37A0AD141C16003600731E50 /* PBXContainerItemProxy */ = {
+ F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9D1001114D8D0BA00099D91;
- remoteInfo = dsc_extractor;
+ remoteGlobalIDString = F9AB02B71F329FAA00EE96C4;
+ remoteInfo = libclosured;
};
- 37A0AD161C16003600731E50 /* PBXContainerItemProxy */ = {
+ F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 377685F21AC4B27D00026E6C;
- remoteInfo = multi_dyld_shared_cache_builder;
+ remoteGlobalIDString = F922C8161F33B73800D8F479;
+ remoteInfo = "libclosured-stub";
};
- 37A0AD181C16003600731E50 /* PBXContainerItemProxy */ = {
+ F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = 3703A1111B38C1B300ADBA7F;
- remoteInfo = dyld_shared_cache_builder;
+ remoteGlobalIDString = F99B8E550FEC10F600701838;
+ remoteInfo = dyld_shared_cache_util;
};
- 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */ = {
+ F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F99B8E550FEC10F600701838;
- remoteInfo = dyld_shared_cache_util;
+ remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+ remoteInfo = libdsc;
};
- F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+ F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
- remoteInfo = libdyld.dylib;
+ remoteGlobalIDString = F9D1001114D8D0BA00099D91;
+ remoteInfo = dsc_extractor;
+ };
+ F96543A01E343601003C5540 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
+ remoteInfo = dyld_closure_util;
};
F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
);
runOnlyForDeploymentPostprocessing = 1;
};
- F908135111D3ED9000626CC1 /* usr|include|mach-o */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 8;
- dstPath = "$(INSTALL_PATH_PREFIX)/usr/include/mach-o";
- dstSubfolderSpec = 0;
- files = (
- F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */,
- F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */,
- );
- name = "usr|include|mach-o";
- runOnlyForDeploymentPostprocessing = 1;
- };
- F908137011D3FB5000626CC1 /* usr|include */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 8;
- dstPath = "$(INSTALL_PATH_PREFIX)/usr/include";
- dstSubfolderSpec = 0;
- files = (
- F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */,
- );
- name = "usr|include";
- runOnlyForDeploymentPostprocessing = 1;
- };
- F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 8;
- dstPath = "$(INSTALL_PATH_PREFIX)/usr/local/include/mach-o";
- dstSubfolderSpec = 0;
- 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 */,
- );
- name = "usr|local|include|mach-o";
- runOnlyForDeploymentPostprocessing = 1;
- };
F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
name = "usr|share|man|man3";
runOnlyForDeploymentPostprocessing = 1;
};
+ F94942B11E67965C0019AE08 /* install man page */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1;
+ dstSubfolderSpec = 0;
+ files = (
+ F94942B31E6796D70019AE08 /* closured.1 in install man page */,
+ );
+ name = "install man page";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ F97C61A51DBAD1A900A84CD7 /* Copy Files */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ name = "Copy Files";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
F97FF3541C23638F000ACDD2 /* install man page */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
/* Begin PBXFileReference section */
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; };
+ 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
+ 37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = "<group>"; };
+ 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
+ 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = "<group>"; };
+ 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = "<group>"; };
+ 37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = "<group>"; };
+ 37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = "<group>"; };
+ 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = "<group>"; };
+ 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = "<group>"; };
+ 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = "<group>"; };
+ 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = "<group>"; };
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; };
EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; };
EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; };
EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.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>"; };
+ F902031F1DEE83C000AC3F76 /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringUtils.h; path = "dyld3/shared-cache/StringUtils.h"; sourceTree = "<group>"; };
+ F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = usr/local/lib/libCrashReporterClient.a; sourceTree = SDKROOT; };
+ F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = com.apple.dyld.closured.sb; path = dyld3/closured/com.apple.dyld.closured.sb; 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>"; };
+ F922C8171F33B73800D8F479 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "libclosured-stub.cpp"; path = "dyld3/libclosured-stub.cpp"; sourceTree = "<group>"; };
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; };
F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; 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>"; };
+ F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = "<group>"; };
+ F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = "<group>"; };
+ F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
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>"; };
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>"; };
+ 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>"; usesTabs = 0; };
F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
+ F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_sim_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_sim_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_sim_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
+ F963546A1DD8D8D300895049 /* ImageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageProxy.h; path = "dyld3/shared-cache/ImageProxy.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F963546B1DD8F2A800895049 /* ImageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageProxy.cpp; path = "dyld3/shared-cache/ImageProxy.cpp"; sourceTree = "<group>"; usesTabs = 0; };
F96D19711D7F63EE007AF3CE /* expand.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = expand.pl; path = bin/expand.pl; sourceTree = "<group>"; };
+ F96D19A51D9363D6007AF3CE /* APIs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs.cpp; path = dyld3/APIs.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F96D19A61D9363D6007AF3CE /* AllImages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AllImages.cpp; path = dyld3/AllImages.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F96D19A71D9363D6007AF3CE /* AllImages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AllImages.h; path = dyld3/AllImages.h; sourceTree = "<group>"; usesTabs = 0; };
+ F96D19A91D94576E007AF3CE /* MachOParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachOParser.h; path = dyld3/MachOParser.h; sourceTree = "<group>"; usesTabs = 0; };
+ F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MachOParser.cpp; path = dyld3/MachOParser.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F96D19C11D95C6D6007AF3CE /* APIs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APIs.h; path = dyld3/APIs.h; sourceTree = "<group>"; usesTabs = 0; };
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>"; };
+ F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheRuntime.cpp; path = dyld3/SharedCacheRuntime.cpp; sourceTree = "<group>"; };
+ F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCacheRuntime.h; path = dyld3/SharedCacheRuntime.h; sourceTree = "<group>"; };
+ F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libdyldEntryVector.h; path = dyld3/libdyldEntryVector.h; sourceTree = "<group>"; usesTabs = 0; };
+ F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = libdyldEntryVector.cpp; path = dyld3/libdyldEntryVector.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = "<group>"; usesTabs = 0; };
+ F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
+ F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = "<group>"; usesTabs = 0; };
+ F98692021DC3EF4800CBEDE6 /* LaunchCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCache.h; path = dyld3/LaunchCache.h; sourceTree = "<group>"; usesTabs = 0; };
+ F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheFormat.h; path = dyld3/LaunchCacheFormat.h; sourceTree = "<group>"; usesTabs = 0; };
+ F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCachePrinter.cpp; path = dyld3/LaunchCachePrinter.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheReader.cpp; path = dyld3/LaunchCacheReader.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheWriter.cpp; path = dyld3/LaunchCacheWriter.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheWriter.h; path = dyld3/LaunchCacheWriter.h; sourceTree = "<group>"; usesTabs = 0; };
+ F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AdjustDylibSegments.cpp; path = "dyld3/shared-cache/AdjustDylibSegments.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldSharedCache.h; path = "dyld3/shared-cache/DyldSharedCache.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtils.cpp; path = "dyld3/shared-cache/FileUtils.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileUtils.h; path = "dyld3/shared-cache/FileUtils.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC1Abstraction.hpp; path = "dyld3/shared-cache/ObjC1Abstraction.hpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC2Abstraction.hpp; path = "dyld3/shared-cache/ObjC2Abstraction.hpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerBranches.cpp; path = "dyld3/shared-cache/OptimizerBranches.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerLinkedit.cpp; path = "dyld3/shared-cache/OptimizerLinkedit.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerObjC.cpp; path = "dyld3/shared-cache/OptimizerObjC.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldSharedCache.cpp; path = "dyld3/shared-cache/DyldSharedCache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Diagnostics.cpp; path = dyld3/Diagnostics.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CacheBuilder.cpp; path = "dyld3/shared-cache/CacheBuilder.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CacheBuilder.h; path = "dyld3/shared-cache/CacheBuilder.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_cache_format.h; path = "dyld3/shared-cache/dyld_cache_format.h"; sourceTree = "<group>"; usesTabs = 0; };
+ F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodeSigningTypes.h; path = dyld3/CodeSigningTypes.h; sourceTree = "<group>"; usesTabs = 0; };
+ F988F0BA1E2FDF5B003AED79 /* execserver.defs */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; 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; };
+ F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */ = {isa = PBXFileReference; explicitFileType = text; name = "dyld-potential-framework-overrides"; path = "dyld3/dyld-potential-framework-overrides"; sourceTree = "<group>"; };
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>"; };
F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = "<group>"; };
F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = "<group>"; };
+ F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; 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; };
+ F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldCacheParser.cpp; path = dyld3/DyldCacheParser.cpp; sourceTree = "<group>"; };
+ F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldCacheParser.h; path = dyld3/DyldCacheParser.h; sourceTree = "<group>"; };
+ F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = make_ios_dyld_cache.cpp; path = "dyld3/shared-cache/make_ios_dyld_cache.cpp"; sourceTree = "<group>"; };
+ F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = "<group>"; };
+ F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = "<group>"; usesTabs = 0; };
+ F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = "<group>"; usesTabs = 0; };
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>"; };
+ F9D862441DC9759C000A199A /* dyld_closure_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_closure_util.cpp; path = "dyld3/shared-cache/dyld_closure_util.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+ F9DDEDAA1E28787900A753DC /* closured.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = closured.cpp; path = dyld3/closured/closured.cpp; sourceTree = "<group>"; };
+ F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = closuredProtocol.defs; path = dyld3/closured/closuredProtocol.defs; sourceTree = "<group>"; };
+ F9DDEDAC1E28787900A753DC /* closuredtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = closuredtypes.h; path = dyld3/closured/closuredtypes.h; sourceTree = "<group>"; };
+ F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.dyld.closured.plist; path = dyld3/closured/com.apple.dyld.closured.plist; sourceTree = "<group>"; };
+ F9DDEDB21E2878CA00A753DC /* closured */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = closured; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
+ F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureBuffer.cpp; path = dyld3/ClosureBuffer.cpp; sourceTree = "<group>"; };
+ F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClosureBuffer.h; path = dyld3/ClosureBuffer.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; };
F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = "include/mach-o/dyld_gdb.h"; sourceTree = SOURCE_ROOT; };
F9ED4CE90630A80600DF4E74 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_priv.h; path = "include/mach-o/dyld_priv.h"; sourceTree = SOURCE_ROOT; };
F9ED4CEA0630A80600DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = "include/mach-o/dyld.h"; sourceTree = SOURCE_ROOT; };
+ F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = update_dyld_shared_cache_entitlements.plist; path = "dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist"; sourceTree = "<group>"; };
+ F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = closured_entitlements.plist; path = dyld3/closured/closured_entitlements.plist; sourceTree = "<group>"; };
+ F9EDC0A01F0481B400B030F4 /* closured.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = closured.xcconfig; sourceTree = "<group>"; };
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>"; };
+ F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathOverrides.cpp; path = dyld3/PathOverrides.cpp; sourceTree = "<group>"; };
+ F9F76FAF1E08CFF200828678 /* PathOverrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathOverrides.h; path = dyld3/PathOverrides.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F922C8141F33B73800D8F479 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F93937300A94FAF700070A07 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */,
+ F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */,
+ F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F963543D1DCD74A400895049 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F97C61A41DBAD1A900A84CD7 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9AB02B51F329FAA00EE96C4 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9D1001014D8D0BA00099D91 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9DDEDAF1E2878CA00A753DC /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
EF799FE8070D27BB00F78484 /* man1 */ = {
isa = PBXGroup;
children = (
+ F94942B21E6796D40019AE08 /* closured.1 */,
EF799FE9070D27BB00F78484 /* dyld.1 */,
F97FF3631C237F5C000ACDD2 /* nocr.1 */,
F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */,
F939373D0A94FC4700070A07 /* launch-cache */ = {
isa = PBXGroup;
children = (
- F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */,
F939373E0A94FC4700070A07 /* Architectures.hpp */,
F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */,
F93937400A94FC4700070A07 /* dyld_cache_format.h */,
F93937410A94FC4700070A07 /* FileAbstraction.hpp */,
F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */,
- F93937440A94FC4700070A07 /* MachOLayout.hpp */,
- F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */,
- F98935B90A9A412B00FB6228 /* MachOBinder.hpp */,
F95C95160E994796007B7CB8 /* MachOTrie.hpp */,
F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
path = "launch-cache";
sourceTree = "<group>";
};
+ F94C22231E513CA90079E5DD /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */,
+ 37F7A5961BB363820039043A /* Bom.framework */,
+ 376ED1D71C46F2710051DD54 /* Metabom.framework */,
+ F94C22241E513CA90079E5DD /* CoreFoundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ F96D19A41D9363B7007AF3CE /* dyld3 */ = {
+ isa = PBXGroup;
+ children = (
+ F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */,
+ F9DDEDA91E28785800A753DC /* closured */,
+ F98692161DC3EF7700CBEDE6 /* shared-cache */,
+ F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */,
+ F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */,
+ F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */,
+ F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */,
+ F9C275581DA71A13007A5D8A /* Loading.cpp */,
+ F9C275591DA71A13007A5D8A /* Loading.h */,
+ F97C61A01D9CA6B800A84CD7 /* Logging.cpp */,
+ F97C61A11D9CA6B800A84CD7 /* Logging.h */,
+ 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */,
+ 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */,
+ F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */,
+ F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */,
+ F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */,
+ F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */,
+ F98692001DC3EF4800CBEDE6 /* Diagnostics.h */,
+ F98692021DC3EF4800CBEDE6 /* LaunchCache.h */,
+ F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */,
+ F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */,
+ F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */,
+ F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */,
+ F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */,
+ F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */,
+ F9F76FAF1E08CFF200828678 /* PathOverrides.h */,
+ F96D19C11D95C6D6007AF3CE /* APIs.h */,
+ F96D19A51D9363D6007AF3CE /* APIs.cpp */,
+ F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */,
+ F96D19A71D9363D6007AF3CE /* AllImages.h */,
+ F96D19A61D9363D6007AF3CE /* AllImages.cpp */,
+ F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */,
+ F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */,
+ F96D19A91D94576E007AF3CE /* MachOParser.h */,
+ F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */,
+ F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */,
+ );
+ name = dyld3;
+ sourceTree = "<group>";
+ };
F971DD121A4A0E0700BBDD52 /* configs */ = {
isa = PBXGroup;
children = (
F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */,
F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
+ F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */,
+ F9EDC0A01F0481B400B030F4 /* closured.xcconfig */,
);
path = configs;
sourceTree = SOURCE_ROOT;
path = nocr;
sourceTree = "<group>";
};
- F9D0FE6C1A367093001E839B /* interlinked-dylibs */ = {
+ F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
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;
+ 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */,
+ 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
+ F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
+ F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */,
+ 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */,
+ 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */,
+ F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
+ F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */,
+ F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
+ F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
+ F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
+ F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
+ F963546A1DD8D8D300895049 /* ImageProxy.h */,
+ F963546B1DD8F2A800895049 /* ImageProxy.cpp */,
+ 37908A2C1E3A85A4009613FA /* Manifest.h */,
+ 37908A281E3A853E009613FA /* Manifest.mm */,
+ F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
+ F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */,
+ F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */,
+ F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */,
+ F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */,
+ F902031F1DEE83C000AC3F76 /* StringUtils.h */,
+ 37908A2D1E3A85A4009613FA /* Trie.hpp */,
+ F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
+ 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
+ 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */,
+ F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */,
+ F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
+ F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
+ F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
+ );
+ name = "shared-cache";
+ sourceTree = "<group>";
+ };
+ F9DDEDA91E28785800A753DC /* closured */ = {
+ isa = PBXGroup;
+ children = (
+ F9DDEDAA1E28787900A753DC /* closured.cpp */,
+ F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */,
+ F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */,
+ F9DDEDAC1E28787900A753DC /* closuredtypes.h */,
+ F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */,
+ F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */,
+ );
+ name = closured;
+ sourceTree = "<group>";
};
F9ED4C870630A72200DF4E74 = {
isa = PBXGroup;
children = (
+ F988F0BA1E2FDF5B003AED79 /* execserver.defs */,
F9F6F4261C1FAF8000BD8FED /* testing */,
F971DD121A4A0E0700BBDD52 /* configs */,
F9ED4CBB0630A7AA00DF4E74 /* src */,
F9ED4CBE0630A7B100DF4E74 /* include */,
F97FF3571C23638F000ACDD2 /* nocr */,
F9ED4C990630A76000DF4E74 /* Products */,
- F9D0FE6C1A367093001E839B /* interlinked-dylibs */,
+ F96D19A41D9363B7007AF3CE /* dyld3 */,
F939373D0A94FC4700070A07 /* launch-cache */,
+ F94C22231E513CA90079E5DD /* Frameworks */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
- usesTabs = 1;
+ usesTabs = 0;
};
F9ED4C990630A76000DF4E74 /* Products */ = {
isa = PBXGroup;
377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
F97FF3561C23638F000ACDD2 /* nocr */,
+ F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
+ F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
+ F9DDEDB21E2878CA00A753DC /* closured */,
+ F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */,
+ F922C8171F33B73800D8F479 /* libclosured.dylib */,
);
name = Products;
sourceTree = "<group>";
F9ED4CBB0630A7AA00DF4E74 /* src */ = {
isa = PBXGroup;
children = (
- F97FF35E1C236402000ACDD2 /* execserver.defs */,
F97FF35F1C236402000ACDD2 /* nocr.c */,
F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
- F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */,
+ F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
F9B01E3D0739ABDE00CF981B /* dyld.exp */,
children = (
F96D19711D7F63EE007AF3CE /* expand.pl */,
F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
- F918691408B16D2500E0F9DB /* dyld-interposing.h */,
F98D274C0AA79D7400416316 /* dyld_images.h */,
+ F918691408B16D2500E0F9DB /* dyld-interposing.h */,
+ F9ED4CEA0630A80600DF4E74 /* dyld.h */,
F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */,
F9ED4CE90630A80600DF4E74 /* dyld_priv.h */,
- F9ED4CEA0630A80600DF4E74 /* dyld.h */,
F99EE6AE06B48D4200BF1992 /* dlfcn.h */,
F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */,
);
};
/* End PBXGroup section */
+/* Begin PBXHeadersBuildPhase section */
+ F922C8151F33B73800D8F479 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F98F1FBB1E4029CA00EF868D /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F99006DD1E411BA70013456D /* dyld_images.h in Headers */,
+ F99006DE1E411BBC0013456D /* dyld.h in Headers */,
+ F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */,
+ F960A78A1E40569400840176 /* dyld-interposing.h in Headers */,
+ F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */,
+ F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */,
+ F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9AB02B61F329FAA00EE96C4 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
/* Begin PBXNativeTarget section */
3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {
isa = PBXNativeTarget;
productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
productType = "com.apple.product-type.tool";
};
+ F922C8161F33B73800D8F479 /* libclosured-stub */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */;
+ buildPhases = (
+ F922C8131F33B73800D8F479 /* Sources */,
+ F922C8141F33B73800D8F479 /* Frameworks */,
+ F922C8151F33B73800D8F479 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "libclosured-stub";
+ productName = "libclosured-stub";
+ productReference = F922C8171F33B73800D8F479 /* libclosured.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
isa = PBXNativeTarget;
buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */;
F93937300A94FAF700070A07 /* Frameworks */,
F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
+ F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */,
);
buildRules = (
);
productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
productType = "com.apple.product-type.tool";
};
+ F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */;
+ buildPhases = (
+ F96354301DCD74A400895049 /* create dyld_cache_config.h */,
+ F96354311DCD74A400895049 /* Sources */,
+ F963543D1DCD74A400895049 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = update_dyld_sim_shared_cache;
+ productName = update_dyld_shared_cache;
+ productReference = F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */;
+ productType = "com.apple.product-type.tool";
+ };
+ F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */;
+ buildPhases = (
+ F97C61A31DBAD1A900A84CD7 /* Sources */,
+ F97C61A41DBAD1A900A84CD7 /* Frameworks */,
+ F97C61A51DBAD1A900A84CD7 /* Copy Files */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dyld_closure_util;
+ productName = dyld_closure_util;
+ productReference = F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */;
+ productType = "com.apple.product-type.tool";
+ };
F97FF3551C23638F000ACDD2 /* nocr */ = {
isa = PBXNativeTarget;
buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */;
productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */;
productType = "com.apple.product-type.tool";
};
+ F9AB02B71F329FAA00EE96C4 /* libclosured */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */;
+ buildPhases = (
+ F9AB02B41F329FAA00EE96C4 /* Sources */,
+ F9AB02B51F329FAA00EE96C4 /* Frameworks */,
+ F9AB02B61F329FAA00EE96C4 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libclosured;
+ productName = libclosured;
+ productReference = F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
F9D1001114D8D0BA00099D91 /* dsc_extractor */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */;
productReference = F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */;
productType = "com.apple.product-type.library.dynamic";
};
+ F9DDEDB11E2878CA00A753DC /* closured */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */;
+ buildPhases = (
+ F9DDEDAE1E2878CA00A753DC /* Sources */,
+ F9DDEDAF1E2878CA00A753DC /* Frameworks */,
+ F94942B01E6794650019AE08 /* installl plist */,
+ F94942B11E67965C0019AE08 /* install man page */,
+ F913C8511E93137700458AA3 /* Install sandbox profile */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = closured;
+ productName = closured;
+ productReference = F9DDEDB21E2878CA00A753DC /* closured */;
+ productType = "com.apple.product-type.tool";
+ };
F9ED4C970630A76000DF4E74 /* dyld */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */;
F921D3160703769A000D1056 /* PBXBuildRule */,
);
dependencies = (
+ F922C8121F33B62700D8F479 /* PBXTargetDependency */,
F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
+ F96543A11E343601003C5540 /* PBXTargetDependency */,
);
name = dyld;
productName = dyld;
buildPhases = (
F9ED4C9C0630A76B00DF4E74 /* Sources */,
F959621018849DF20003E4D4 /* add dyld symlink */,
+ F98F1FBB1E4029CA00EF868D /* Headers */,
+ F960A78C1E405E2300840176 /* expand dyld_priv.h macros */,
+ F99006DF1E411C500013456D /* install dlfcn.h */,
);
buildRules = (
F921D31E070376F1000D1056 /* PBXBuildRule */,
F9574C4906C94DA700142BFA /* PBXBuildRule */,
);
dependencies = (
+ F922C81E1F33B96300D8F479 /* PBXTargetDependency */,
);
name = libdyld.dylib;
productName = libdyld;
F9ED4C8B0630A72300DF4E74 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0630;
+ LastUpgradeCheck = 0900;
TargetAttributes = {
37A0AD0A1C15FFF500731E50 = {
- CreatedOnToolsVersion = 7.0;
+ CreatedOnToolsVersion = 8.0;
+ };
+ F922C8161F33B73800D8F479 = {
+ CreatedOnToolsVersion = 9.0;
+ };
+ F97C61A61DBAD1A900A84CD7 = {
+ CreatedOnToolsVersion = 8.0;
+ DevelopmentTeam = 59GAB85EFG;
+ ProvisioningStyle = Automatic;
};
F97FF3551C23638F000ACDD2 = {
- CreatedOnToolsVersion = 7.1;
+ CreatedOnToolsVersion = 8.0;
+ };
+ F9AB02B71F329FAA00EE96C4 = {
+ CreatedOnToolsVersion = 9.0;
+ };
+ F9DDEDB11E2878CA00A753DC = {
+ CreatedOnToolsVersion = 8.2;
+ DevelopmentTeam = 59GAB85EFG;
+ ProvisioningStyle = Automatic;
};
F9F6F4271C1FB0A700BD8FED = {
- CreatedOnToolsVersion = 7.1;
+ CreatedOnToolsVersion = 8.0;
};
};
};
targets = (
F9ED4C920630A73900DF4E74 /* all */,
37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
- F9ED4C970630A76000DF4E74 /* dyld */,
F908134211D3ED0B00626CC1 /* libdyld */,
+ F9ED4C970630A76000DF4E74 /* dyld */,
+ F9DDEDB11E2878CA00A753DC /* closured */,
F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
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 */,
+ F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */,
+ F99B8E550FEC10F600701838 /* dyld_shared_cache_util */,
+ F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */,
+ F9F2A5580F7AEE9800B7C9EB /* libdsc */,
+ F9D1001114D8D0BA00099D91 /* dsc_extractor */,
F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
F97FF3551C23638F000ACDD2 /* nocr */,
+ F9AB02B71F329FAA00EE96C4 /* libclosured */,
+ F922C8161F33B73800D8F479 /* libclosured-stub */,
);
};
/* End PBXProject section */
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;
};
+ F913C8511E93137700458AA3 /* Install sandbox profile */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.sb",
+ );
+ name = "Install sandbox profile";
+ outputPaths = (
+ "${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "if [ ${OS} = \"MACOS\" ]; then\n mkdir -p ${DSTROOT}/System/Library/Sandbox/Profiles\n cp ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.sb ${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb\nfi";
+ showEnvVarsInLog = 0;
+ };
F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
shellScript = "if [ \"${PRODUCT_NAME}\" = \"dyld_sim\" ]\nthen\n /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n";
showEnvVarsInLog = 0;
};
+ F94182D61E60E74E00D8EF25 /* pre-platform builds */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "pre-platform builds";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "xcodebuild install -target multi_dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\nif [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\txcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\txcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nelse\n\txcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi";
+ showEnvVarsInLog = 0;
+ };
+ F94942B01E6794650019AE08 /* installl plist */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.plist",
+ );
+ name = "installl plist";
+ outputPaths = (
+ "${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "mkdir -p ${DSTROOT}/System/Library/LaunchDaemons\nplutil -convert binary1 -o ${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.plist";
+ showEnvVarsInLog = 0;
+ };
F959621018849DF20003E4D4 /* add dyld symlink */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
shellScript = "if [[ \"${PLATFORM_NAME}\" == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
showEnvVarsInLog = 0;
};
- F96D19701D7F62C3007AF3CE /* Install dyld_priv.h */ = {
+ F960A78C1E405E2300840176 /* expand dyld_priv.h macros */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
+ "${SRCROOT}/include/mach-o/dyld_priv.h",
);
- name = "Install dyld_priv.h";
+ name = "expand dyld_priv.h macros";
outputPaths = (
+ "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h",
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/${INSTALL_PATH_PREFIX}/usr/local/include/mach-o/dyld_priv.h\n";
+ shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n";
+ showEnvVarsInLog = 0;
+ };
+ F96354301DCD74A400895049 /* create dyld_cache_config.h */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "create 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\n";
showEnvVarsInLog = 0;
};
F96D19A31D91D733007AF3CE /* make dyld_priv.h */ = {
shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n";
showEnvVarsInLog = 0;
};
- F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
+ F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
);
- name = "do not install duplicates";
+ name = "mkdir /var/db/dyld";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\n\trm -rf ${DSTROOT}/usr/lib\nfi\n";
+ shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld";
showEnvVarsInLog = 0;
};
- F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+ F99006DF1E411C500013456D /* install dlfcn.h */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
);
- name = "suppress macosx dyld_shared_cache_util";
+ name = "install dlfcn.h";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\nfi\n";
+ shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/usr/include/\n";
showEnvVarsInLog = 0;
};
- F9D050C811DD701A00FB0A29 /* configure archives */ = {
+ F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
isa = PBXShellScriptBuildPhase;
- buildActionMask = 12;
+ buildActionMask = 8;
files = (
);
inputPaths = (
);
- name = "configure archives";
+ name = "do not install duplicates";
outputPaths = (
- "$(DERIVED_SOURCES_DIR)/archives.txt",
);
- runOnlyForDeploymentPostprocessing = 0;
+ runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- 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";
+ shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\n\trm -rf ${DSTROOT}/usr/lib\nfi";
showEnvVarsInLog = 0;
};
- F9F6F42B1C1FB0AE00BD8FED /* build */ = {
+ F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "suppress macosx dyld_shared_cache_util";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\n mkdir -p ${DSTROOT}/AppleInternal/Library/Preferences/\n cp dyld3/dyld-potential-framework-overrides ${DSTROOT}/AppleInternal/Library/Preferences/\nfi\n";
+ showEnvVarsInLog = 0;
+ };
+ F9D050C811DD701A00FB0A29 /* configure archives */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 12;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "configure archives";
+ outputPaths = (
+ "$(DERIVED_SOURCES_DIR)/archives.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ 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 = (
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 */,
+ 37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+ 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */,
+ 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */,
+ 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */,
+ 37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */,
+ 37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */,
+ 37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */,
+ 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */,
+ 37908A311E3EB585009613FA /* MachOParser.cpp in Sources */,
+ F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */,
+ 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
+ 37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */,
+ 37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */,
+ 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */,
+ 37908A321E3ED667009613FA /* FileUtils.cpp in Sources */,
+ 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */,
+ 37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
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 */,
+ 37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+ 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */,
+ 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */,
+ 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */,
+ 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */,
+ 37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */,
+ 37554F401E3F167A00407388 /* MachOParser.cpp in Sources */,
+ F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */,
+ 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */,
+ 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */,
+ 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
+ 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */,
+ 37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */,
+ 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */,
+ 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */,
+ 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */,
+ 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F922C8131F33B73800D8F479 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 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 */,
+ F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */,
+ F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */,
+ F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */,
+ F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */,
+ F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */,
+ F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */,
+ F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */,
+ F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */,
+ F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */,
+ F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */,
+ F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */,
+ F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */,
+ F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */,
+ F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */,
+ F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F96354311DCD74A400895049 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */,
+ F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */,
+ F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */,
+ F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */,
+ F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */,
+ F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */,
+ F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */,
+ F96354361DCD74A400895049 /* FileUtils.cpp in Sources */,
+ F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */,
+ F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */,
+ F96354381DCD74A400895049 /* MachOParser.cpp in Sources */,
+ F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */,
+ F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */,
+ 37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */,
+ F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */,
+ F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F97C61A31DBAD1A900A84CD7 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */,
+ F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */,
+ F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */,
+ F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */,
+ F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */,
+ F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */,
+ F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */,
+ F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */,
+ F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */,
+ F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */,
+ F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */,
+ F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */,
+ F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F97FF3601C236408000ACDD2 /* execserver.defs in Sources */,
F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
+ F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9AB02B41F329FAA00EE96C4 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */,
+ F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */,
+ F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */,
+ F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */,
+ F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */,
+ F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */,
+ F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */,
+ F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */,
+ F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */,
+ F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9D1000F14D8D0BA00099D91 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F9DDEDAE1E2878CA00A753DC /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */,
+ F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */,
+ F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */,
+ F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */,
+ F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */,
+ F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */,
+ F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */,
+ F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */,
+ F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */,
+ F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */,
+ F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */,
+ F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9ED4C950630A76000DF4E74 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */,
F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */,
F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */,
F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */,
F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */,
F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */,
F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */,
+ F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */,
+ F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */,
+ F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */,
+ F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */,
+ F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */,
+ F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */,
+ F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */,
+ F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */,
+ 37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */,
F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.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 */,
+ F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */,
+ F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */,
+ F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */,
+ F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */,
+ F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */,
+ F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */,
+ F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */,
+ F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */,
+ F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */,
+ F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */,
+ F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */,
+ F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */,
+ F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */,
+ F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
};
- 37A0AD111C16003600731E50 /* PBXTargetDependency */ = {
+ D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */;
- targetProxy = 37A0AD101C16003600731E50 /* PBXContainerItemProxy */;
+ target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
+ targetProxy = D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */;
};
- 37A0AD131C16003600731E50 /* PBXTargetDependency */ = {
+ F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
- targetProxy = 37A0AD121C16003600731E50 /* PBXContainerItemProxy */;
+ target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
+ targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
};
- 37A0AD151C16003600731E50 /* PBXTargetDependency */ = {
+ F922C8121F33B62700D8F479 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
- targetProxy = 37A0AD141C16003600731E50 /* PBXContainerItemProxy */;
+ target = F9AB02B71F329FAA00EE96C4 /* libclosured */;
+ targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */;
};
- 37A0AD171C16003600731E50 /* PBXTargetDependency */ = {
+ F922C81E1F33B96300D8F479 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
- targetProxy = 37A0AD161C16003600731E50 /* PBXContainerItemProxy */;
+ target = F922C8161F33B73800D8F479 /* libclosured-stub */;
+ targetProxy = F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */;
};
- 37A0AD191C16003600731E50 /* PBXTargetDependency */ = {
+ F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */;
- targetProxy = 37A0AD181C16003600731E50 /* PBXContainerItemProxy */;
+ target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+ targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */;
};
- 37A0AD1B1C16004600731E50 /* PBXTargetDependency */ = {
+ F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
- targetProxy = 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */;
+ target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+ targetProxy = F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */;
};
- F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+ F94182DC1E60F16900D8EF25 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
- targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
+ target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
+ targetProxy = F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */;
+ };
+ F96543A11E343601003C5540 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
+ targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */;
};
F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
+ "BUILDING_CACHE_BUILDER=1",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-DBOM_SUPPORT=1";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = macosx;
+ TOOLCHAINS = default;
USER_HEADER_SEARCH_PATHS = "../launch-cache/";
- VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ VALID_ARCHS = "x86_64 x86_64h";
};
name = Debug;
};
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "-DBOM_SUPPORT=1";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = macosx;
+ TOOLCHAINS = default;
USER_HEADER_SEARCH_PATHS = "../launch-cache/";
- VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ VALID_ARCHS = "x86_64 x86_64h";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
+ "BUILDING_CACHE_BUILDER=1",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-DBOM_SUPPORT=1";
SDKROOT = macosx.internal;
STRIP_INSTALLED_PRODUCT = NO;
USER_HEADER_SEARCH_PATHS = "../launch-cache/";
- VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+ VALID_ARCHS = "x86_64 x86_64h";
};
name = Debug;
};
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"$(SDKROOT)/AppleInternal/Library/Frameworks",
);
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
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";
+ VALID_ARCHS = "x86_64 x86_64h";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
name = Release;
};
+ F922C8191F33B73800D8F479 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ ENABLE_TESTABILITY = YES;
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ OTHER_LDFLAGS = "-nostdlib";
+ PRODUCT_NAME = closured;
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ F922C81A1F33B73800D8F479 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ ENABLE_NS_ASSERTIONS = NO;
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_LDFLAGS = "-nostdlib";
+ PRODUCT_NAME = closured;
+ SDKROOT = macosx;
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
F93937350A94FB2900070A07 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
GCC_OPTIMIZATION_LEVEL = 0;
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
+ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/include",
+ "$(SRCROOT)/dyld3",
+ "$(SRCROOT)/dyld3/shared-cache",
+ );
+ INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
SDKROOT = macosx.internal;
+ USE_HEADERMAP = NO;
VALID_ARCHS = x86_64;
};
name = Debug;
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
+ CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
GCC_OPTIMIZATION_LEVEL = s;
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+ GCC_WARN_SHADOW = YES;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
- HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/include",
+ "$(SRCROOT)/dyld3",
+ "$(SRCROOT)/dyld3/shared-cache",
+ );
+ INSTALL_PATH = /usr/bin;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
SDKROOT = macosx.internal;
STRIP_INSTALLED_PRODUCT = YES;
STRIP_STYLE = debugging;
+ USE_HEADERMAP = NO;
VALID_ARCHS = x86_64;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
+ F96354431DCD74A400895049 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ 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_THREADSAFE_STATICS = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
+ GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
+ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+ GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/include",
+ "$(SRCROOT)/dyld3",
+ "$(SRCROOT)/dyld3/shared-cache",
+ );
+ INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "-stdlib=libc++";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
+ USE_HEADERMAP = NO;
+ VALID_ARCHS = x86_64;
+ };
+ name = Debug;
+ };
+ F96354441DCD74A400895049 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */;
+ buildSettings = {
+ 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_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(SRCROOT)/include",
+ "$(SRCROOT)/dyld3",
+ "$(SRCROOT)/dyld3/shared-cache",
+ );
+ INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
+ OTHER_LDFLAGS = "-stdlib=libc++";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
+ STRIP_INSTALLED_PRODUCT = YES;
+ STRIP_STYLE = debugging;
+ USE_HEADERMAP = NO;
+ VALID_ARCHS = x86_64;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+ F97C61AC1DBAD1A900A84CD7 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = 59GAB85EFG;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Debug;
+ };
+ F97C61AD1DBAD1A900A84CD7 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = 59GAB85EFG;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Release;
+ };
F97FF35A1C23638F000ACDD2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
);
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
PRODUCT_NAME = dyld_shared_cache_util;
+ SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = macosx;
};
name = Debug;
};
);
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
PRODUCT_NAME = dyld_shared_cache_util;
+ SDKROOT = macosx.internal;
SKIP_INSTALL = NO;
+ SUPPORTED_PLATFORMS = macosx;
+ };
+ name = Release;
+ };
+ F9AB02C01F329FAA00EE96C4 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ ENABLE_TESTABILITY = YES;
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_CPP_EXCEPTIONS = YES;
+ GCC_ENABLE_CPP_RTTI = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DYLD_IN_PROCESS=0",
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INSTALL_PATH = /usr/lib/closure;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ OTHER_LDFLAGS = (
+ "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE",
+ "-Wl,-no_inits",
+ );
+ PRODUCT_NAME = closured;
+ SDKROOT = macosx.internal;
+ };
+ name = Debug;
+ };
+ F9AB02C11F329FAA00EE96C4 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ ENABLE_NS_ASSERTIONS = NO;
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_ENABLE_CPP_EXCEPTIONS = YES;
+ GCC_ENABLE_CPP_RTTI = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = "DYLD_IN_PROCESS=0";
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INSTALL_PATH = /usr/lib/closure;
+ MACOSX_DEPLOYMENT_TARGET = 10.12;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_LDFLAGS = (
+ "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE",
+ "-Wl,-no_inits",
+ );
+ PRODUCT_NAME = closured;
+ SDKROOT = macosx.internal;
};
name = Release;
};
baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_BUILTIN_FUNCTIONS = NO;
GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DYLD_VERSION=$(RC_ProjectSourceVersion)",
+ "_LIBCPP_NO_EXCEPTIONS=1",
+ "$(inherited)",
+ );
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
"$(ALIGNMENT)",
"$(ENTRY)",
);
- SDKROOT = macosx.internal;
STRIPFLAGS = "-S";
UNSTRIPPED_PRODUCT = NO;
VERSIONING_SYSTEM = "apple-generic";
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_OPTIMIZATION_LEVEL = s;
- GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DYLD_VERSION=$(RC_ProjectSourceVersion)",
+ "_LIBCPP_NO_EXCEPTIONS=1",
+ "$(inherited)",
+ );
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_SHADOW = YES;
"$(ALIGNMENT)",
"$(ENTRY)",
"-Wl,-no_data_const",
- "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__common:__bss",
+ "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__bss:__common",
);
- SDKROOT = macosx.internal;
STRIPFLAGS = "-S";
UNSTRIPPED_PRODUCT = NO;
VERSIONING_SYSTEM = "apple-generic";
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_EMPTY_BODY = YES;
+ CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 500;
DEAD_CODE_STRIPPING = YES;
EXECUTABLE_PREFIX = lib;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GENERATE_TEXT_BASED_STUBS = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
INSTALLHDRS_COPY_PHASE = YES;
+ INSTALLHDRS_SCRIPT_PHASE = YES;
+ ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-Wglobal-constructors",
+ );
OTHER_LDFLAGS = (
+ "-Wl,-no_inits",
"-nostdlib",
"$(LIBSYSTEM_LIBS)",
"-umbrella",
System,
"-L$(SDKROOT)/usr/lib/system",
);
+ OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 ";
+ PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
PRODUCT_NAME = dyld;
+ PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
SKIP_INSTALL = NO;
+ SUPPORTS_TEXT_BASED_API = YES;
+ TAPI_VERIFY_MODE = Pedantic;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
WARNING_CFLAGS = (
"-Wmost",
"-Wno-four-char-constants",
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_EMPTY_BODY = YES;
+ CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GENERATE_TEXT_BASED_STUBS = YES;
HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
INSTALLHDRS_COPY_PHASE = YES;
+ INSTALLHDRS_SCRIPT_PHASE = YES;
OTHER_CFLAGS = "";
OTHER_CPLUSPLUSFLAGS = (
"-fno-exceptions",
"$(OTHER_CFLAGS)",
);
OTHER_LDFLAGS = (
+ "-Wl,-no_inits",
"-nostdlib",
"$(LIBSYSTEM_LIBS)",
"-umbrella",
System,
"-L$(SDKROOT)/usr/lib/system",
+ "-Wl,-no_inits",
);
"OTHER_LDFLAGS[sdk=iphoneos*]" = (
+ "-Wl,-no_inits",
"-nostdlib",
"$(LIBSYSTEM_LIBS)",
"-umbrella",
"-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty",
);
"OTHER_LDFLAGS[sdk=macosx*]" = (
+ "-Wl,-no_inits",
"-nostdlib",
"$(LIBSYSTEM_LIBS)",
"-umbrella",
System,
"-L$(SDKROOT)/usr/lib/system",
);
+ OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 ";
+ PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
PRODUCT_NAME = dyld;
+ PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
SEPARATE_STRIP = YES;
SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES;
+ SUPPORTS_TEXT_BASED_API = YES;
+ TAPI_VERIFY_MODE = Pedantic;
VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
WARNING_CFLAGS = (
"-Wmost",
"-Wno-four-char-constants",
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LIBRARY = "compiler-default";
- ONLY_ACTIVE_ARCH = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = NO;
+ EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests";
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
};
name = Debug;
};
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LIBRARY = "compiler-default";
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = NO;
+ EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests";
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx.internal;
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
+ };
+ name = Release;
+ };
+ F9DDEDB71E2878CB00A753DC /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = 59GAB85EFG;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "BUILDING_CLOSURED=1",
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ INSTALL_PATH = /usr/libexec/;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ PLIST_FILE_OUTPUT_FORMAT = binary;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ STRIPFLAGS = "-S";
+ VERSIONING_SYSTEM = "";
+ };
+ name = Debug;
+ };
+ F9DDEDB81E2878CB00A753DC /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = 59GAB85EFG;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CLOSURED=1";
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ INSTALL_PATH = /usr/libexec/;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PLIST_FILE_OUTPUT_FORMAT = binary;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ STRIPFLAGS = "-S";
+ VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F922C8191F33B73800D8F479 /* Debug */,
+ F922C81A1F33B73800D8F479 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F96354431DCD74A400895049 /* Debug */,
+ F96354441DCD74A400895049 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F97C61AC1DBAD1A900A84CD7 /* Debug */,
+ F97C61AD1DBAD1A900A84CD7 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9AB02C01F329FAA00EE96C4 /* Debug */,
+ F9AB02C11F329FAA00EE96C4 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9DDEDB71E2878CB00A753DC /* Debug */,
+ F9DDEDB81E2878CB00A753DC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <_simple.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <TargetConditionals.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <dispatch/dispatch.h>
+
+#include <algorithm>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "Logging.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "PathOverrides.h"
+#include "APIs.h"
+#include "StringUtils.h"
+
+
+
+extern "C" {
+ #include "closuredProtocol.h"
+}
+
+
+namespace dyld {
+ extern dyld_all_image_infos dyld_all_image_infos;
+}
+
+
+namespace dyld3 {
+
+
+uint32_t _dyld_image_count(void)
+{
+ log_apis("_dyld_image_count()\n");
+
+ return gAllImages.count();
+}
+
+const mach_header* _dyld_get_image_header(uint32_t imageIndex)
+{
+ log_apis("_dyld_get_image_header(%d)\n", imageIndex);
+
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
+ if ( image.valid() )
+ return loadAddress;
+ return nullptr;
+}
+
+intptr_t _dyld_get_image_slide(const mach_header* mh)
+{
+ log_apis("_dyld_get_image_slide(%p)\n", mh);
+
+ MachOParser parser(mh);
+ return parser.getSlide();
+}
+
+intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
+{
+ log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex);
+
+ const mach_header* mh = _dyld_get_image_header(imageIndex);
+ if ( mh != nullptr )
+ return dyld3::_dyld_get_image_slide(mh);
+ return 0;
+}
+
+const char* _dyld_get_image_name(uint32_t imageIndex)
+{
+ log_apis("_dyld_get_image_name(%d)\n", imageIndex);
+
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
+ if ( image.valid() )
+ return gAllImages.imagePath(image.binaryData());
+ return nullptr;
+}
+
+
+static bool nameMatch(const char* installName, const char* libraryName)
+{
+ const char* leafName = strrchr(installName, '/');
+ if ( leafName == NULL )
+ leafName = installName;
+ else
+ leafName++;
+
+ // -framework case is exact match of leaf name
+ if ( strcmp(leafName, libraryName) == 0 )
+ return true;
+
+ // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
+ size_t leafNameLen = strlen(leafName);
+ size_t libraryNameLen = strlen(libraryName);
+ if ( leafNameLen < (libraryNameLen+9) )
+ return false;
+ if ( strncmp(leafName, "lib", 3) != 0 )
+ return false;
+ if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 )
+ return false;
+ if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 )
+ return false;
+ return (leafName[libraryNameLen+3] == '.');
+}
+
+
+//
+// BETTER, USE: dyld_get_program_sdk_version()
+//
+// Scans the main executable and returns the version of the specified dylib the program was built against.
+//
+// The library to find is the leaf name that would have been passed to linker tool
+// (e.g. -lfoo or -framework foo would use "foo").
+//
+// Returns -1 if the main executable did not link against the specified library, or is malformed.
+//
+int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
+{
+ log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName);
+
+ __block int32_t result = -1;
+ MachOParser parser(gAllImages.mainExecutable());
+ parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+ if ( nameMatch(loadPath, libraryName) )
+ result = currentVersion;
+ });
+ log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result);
+ return result;
+}
+
+
+//
+// Searches loaded images for the requested dylib and returns its current version.
+//
+// The library to find is the leaf name that would have been passed to linker tool
+// (e.g. -lfoo or -framework foo would use "foo").
+//
+// If the specified library is not loaded, -1 is returned.
+//
+int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
+{
+ log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName);
+
+ uint32_t count = gAllImages.count();
+ for (uint32_t i=0; i < count; ++i) {
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByLoadOrder(i, &loadAddress);
+ if ( image.valid() ) {
+ MachOParser parser(loadAddress);
+ const char* installName;
+ uint32_t currentVersion;
+ uint32_t compatVersion;
+ if ( parser.getDylibInstallName(&installName, &compatVersion, ¤tVersion) && nameMatch(installName, libraryName) ) {
+ log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", currentVersion);
+ return currentVersion;
+ }
+ }
+ }
+ log_apis(" NSVersionOfRunTimeLibrary() => -1\n");
+ return -1;
+}
+
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+
+static uint32_t watchVersToIOSVers(uint32_t vers)
+{
+ return vers + 0x00070000;
+}
+
+uint32_t dyld_get_program_sdk_watch_os_version()
+{
+ log_apis("dyld_get_program_sdk_watch_os_version()\n");
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ MachOParser parser(gAllImages.mainExecutable());
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ if ( platform == Platform::watchOS )
+ return sdk;
+ }
+ return 0;
+}
+
+uint32_t dyld_get_program_min_watch_os_version()
+{
+ log_apis("dyld_get_program_min_watch_os_version()\n");
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ MachOParser parser(gAllImages.mainExecutable());
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ if ( platform == Platform::watchOS )
+ return minOS; // return raw minOS (not mapped to iOS version)
+ }
+ return 0;
+}
+#endif
+
+
+#if TARGET_OS_BRIDGE
+
+static uint32_t bridgeVersToIOSVers(uint32_t vers)
+{
+ return vers + 0x00090000;
+}
+
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+ log_apis("dyld_get_program_sdk_bridge_os_version()\n");
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ MachOParser parser(gAllImages.mainExecutable());
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ if ( platform == Platform::bridgeOS )
+ return sdk;
+ }
+ return 0;
+}
+
+uint32_t dyld_get_program_min_bridge_os_version()
+{
+ log_apis("dyld_get_program_min_bridge_os_version()\n");
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ MachOParser parser(gAllImages.mainExecutable());
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ if ( platform == Platform::bridgeOS )
+ return minOS; // return raw minOS (not mapped to iOS version)
+ }
+ return 0;
+}
+
+#endif
+
+
+#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
+
+#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+
+static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
+{
+ __block uint32_t foundationVers = 0;
+ __block uint32_t libSystemVers = 0;
+ MachOParser parser(mh);
+ parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+ if ( strcmp(loadPath, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") == 0 )
+ foundationVers = currentVersion;
+ else if ( strcmp(loadPath, "/usr/lib/libSystem.B.dylib") == 0 )
+ libSystemVers = currentVersion;
+ });
+
+ struct DylibToOSMapping {
+ uint32_t dylibVersion;
+ uint32_t osVersion;
+ };
+
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED
+ static const DylibToOSMapping foundationMapping[] = {
+ { PACKED_VERSION(678,24,0), 0x00020000 },
+ { PACKED_VERSION(678,26,0), 0x00020100 },
+ { PACKED_VERSION(678,29,0), 0x00020200 },
+ { PACKED_VERSION(678,47,0), 0x00030000 },
+ { PACKED_VERSION(678,51,0), 0x00030100 },
+ { PACKED_VERSION(678,60,0), 0x00030200 },
+ { PACKED_VERSION(751,32,0), 0x00040000 },
+ { PACKED_VERSION(751,37,0), 0x00040100 },
+ { PACKED_VERSION(751,49,0), 0x00040200 },
+ { PACKED_VERSION(751,58,0), 0x00040300 },
+ { PACKED_VERSION(881,0,0), 0x00050000 },
+ { PACKED_VERSION(890,1,0), 0x00050100 },
+ { PACKED_VERSION(992,0,0), 0x00060000 },
+ { PACKED_VERSION(993,0,0), 0x00060100 },
+ { PACKED_VERSION(1038,14,0),0x00070000 },
+ { PACKED_VERSION(0,0,0), 0x00070000 }
+ // We don't need to expand this table because all recent
+ // binaries have LC_VERSION_MIN_ load command.
+ };
+
+ if ( foundationVers != 0 ) {
+ uint32_t lastOsVersion = 0;
+ for (const DylibToOSMapping* p=foundationMapping; ; ++p) {
+ if ( p->dylibVersion == 0 )
+ return p->osVersion;
+ if ( foundationVers < p->dylibVersion )
+ return lastOsVersion;
+ lastOsVersion = p->osVersion;
+ }
+ }
+
+ #else
+ // Note: versions are for the GM release. The last entry should
+ // always be zero. At the start of the next major version,
+ // a new last entry needs to be added and the previous zero
+ // updated to the GM dylib version.
+ static const DylibToOSMapping libSystemMapping[] = {
+ { PACKED_VERSION(88,1,3), 0x000A0400 },
+ { PACKED_VERSION(111,0,0), 0x000A0500 },
+ { PACKED_VERSION(123,0,0), 0x000A0600 },
+ { PACKED_VERSION(159,0,0), 0x000A0700 },
+ { PACKED_VERSION(169,3,0), 0x000A0800 },
+ { PACKED_VERSION(1197,0,0), 0x000A0900 },
+ { PACKED_VERSION(0,0,0), 0x000A0900 }
+ // We don't need to expand this table because all recent
+ // binaries have LC_VERSION_MIN_ load command.
+ };
+
+ if ( libSystemVers != 0 ) {
+ uint32_t lastOsVersion = 0;
+ for (const DylibToOSMapping* p=libSystemMapping; ; ++p) {
+ if ( p->dylibVersion == 0 )
+ return p->osVersion;
+ if ( libSystemVers < p->dylibVersion )
+ return lastOsVersion;
+ lastOsVersion = p->osVersion;
+ }
+ }
+ #endif
+ return 0;
+}
+#endif
+
+
+//
+// Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
+// specified binary was built against.
+//
+// First looks for LC_VERSION_MIN_* in binary and if sdk field is
+// not zero, return that value.
+// Otherwise, looks for the libSystem.B.dylib the binary linked
+// against and uses a table to convert that to an sdk version.
+//
+uint32_t dyld_get_sdk_version(const mach_header* mh)
+{
+ log_apis("dyld_get_sdk_version(%p)\n", mh);
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+ return 0;
+ MachOParser parser(mh);
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ switch (platform) {
+#if TARGET_OS_BRIDGE
+ case Platform::bridgeOS:
+ // new binary. sdk version looks like "2.0" but API wants "11.0"
+ return bridgeVersToIOSVers(sdk);
+ case Platform::iOS:
+ // old binary. sdk matches API semantics so can return directly.
+ return sdk;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+ case Platform::watchOS:
+ // new binary. sdk version looks like "2.0" but API wants "9.0"
+ return watchVersToIOSVers(sdk);
+ case Platform::iOS:
+ // old binary. sdk matches API semantics so can return directly.
+ return sdk;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+ case Platform::tvOS:
+ case Platform::iOS:
+ return sdk;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+ case Platform::iOS:
+ if ( sdk != 0 ) // old binaries might not have SDK set
+ return sdk;
+ break;
+#else
+ case Platform::macOS:
+ if ( sdk != 0 ) // old binaries might not have SDK set
+ return sdk;
+ break;
+#endif
+ default:
+ // wrong binary for this platform
+ break;
+ }
+ }
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
+ // All watchOS and tvOS binaries should have version load command.
+ return 0;
+#else
+ // MacOSX and iOS have old binaries without version load commmand.
+ return deriveSDKVersFromDylibs(mh);
+#endif
+}
+
+uint32_t dyld_get_program_sdk_version()
+{
+ log_apis("dyld_get_program_sdk_version()\n");
+
+ return dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
+}
+
+uint32_t dyld_get_min_os_version(const mach_header* mh)
+{
+ log_apis("dyld_get_min_os_version(%p)\n", mh);
+
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+ return 0;
+ MachOParser parser(mh);
+ if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ switch (platform) {
+#if TARGET_OS_BRIDGE
+ case Platform::bridgeOS:
+ // new binary. sdk version looks like "2.0" but API wants "11.0"
+ return bridgeVersToIOSVers(minOS);
+ case Platform::iOS:
+ // old binary. sdk matches API semantics so can return directly.
+ return minOS;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+ case Platform::watchOS:
+ // new binary. OS version looks like "2.0" but API wants "9.0"
+ return watchVersToIOSVers(minOS);
+ case Platform::iOS:
+ // old binary. OS matches API semantics so can return directly.
+ return minOS;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+ case Platform::tvOS:
+ case Platform::iOS:
+ return minOS;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+ case Platform::iOS:
+ return minOS;
+#else
+ case Platform::macOS:
+ return minOS;
+#endif
+ default:
+ // wrong binary for this platform
+ break;
+ }
+ }
+ return 0;
+}
+
+
+uint32_t dyld_get_program_min_os_version()
+{
+ log_apis("dyld_get_program_min_os_version()\n");
+
+ return dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
+}
+
+
+bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
+{
+ log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
+
+ if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+ return false;
+ MachOParser parser(mh);
+ return parser.getUuid(uuid);
+}
+
+//
+// _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
+// should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
+// and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
+// to the size required.
+//
+int _NSGetExecutablePath(char* buf, uint32_t* bufsize)
+{
+ log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
+
+ launch_cache::Image image = gAllImages.mainExecutableImage();
+ if ( image.valid() ) {
+ const char* path = gAllImages.imagePath(image.binaryData());
+ size_t pathSize = strlen(path) + 1;
+ if ( *bufsize >= pathSize ) {
+ strcpy(buf, path);
+ return 0;
+ }
+ *bufsize = (uint32_t)pathSize;
+ }
+ return -1;
+}
+
+void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
+{
+ log_apis("_dyld_register_func_for_add_image(%p)\n", func);
+
+ gAllImages.addLoadNotifier(func);
+}
+
+void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
+{
+ log_apis("_dyld_register_func_for_remove_image(%p)\n", func);
+
+ gAllImages.addUnloadNotifier(func);
+}
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
+ _dyld_objc_notify_init init,
+ _dyld_objc_notify_unmapped unmapped)
+{
+ log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
+
+ gAllImages.setObjCNotifiers(mapped, init, unmapped);
+}
+
+
+const mach_header* dyld_image_header_containing_address(const void* addr)
+{
+ log_apis("dyld_image_header_containing_address(%p)\n", addr);
+
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+ if ( image.valid() )
+ return loadAddress;
+ return nullptr;
+}
+
+
+const char* dyld_image_path_containing_address(const void* addr)
+{
+ log_apis("dyld_image_path_containing_address(%p)\n", addr);
+
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+ if ( image.valid() ) {
+ const char* path = gAllImages.imagePath(image.binaryData());
+ log_apis(" dyld_image_path_containing_address() => %s\n", path);
+ return path;
+ }
+ log_apis(" dyld_image_path_containing_address() => NULL\n");
+ return nullptr;
+}
+
+bool _dyld_is_memory_immutable(const void* addr, size_t length)
+{
+ uintptr_t checkStart = (uintptr_t)addr;
+ uintptr_t checkEnd = checkStart + length;
+
+ // quick check to see if in r/o region of shared cache. If so return true.
+ const DyldSharedCache* cache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+ if ( cache != nullptr ) {
+ __block bool firstVMAddr = 0;
+ __block bool isReadOnlyInCache = false;
+ __block bool isInCache = false;
+ cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if ( firstVMAddr == 0 )
+ firstVMAddr = vmAddr;
+ uintptr_t regionStart = (uintptr_t)cache + (uintptr_t)(vmAddr - firstVMAddr);
+ uintptr_t regionEnd = regionStart + (uintptr_t)size;
+ if ( (regionStart < checkStart) && (checkEnd < regionEnd) ) {
+ isInCache = true;
+ isReadOnlyInCache = ((permissions & VM_PROT_WRITE) != 0);
+ }
+ });
+ if ( isInCache )
+ return isReadOnlyInCache;
+ }
+
+ // go slow route of looking at each image's segments
+ const mach_header* loadAddress;
+ uint8_t permissions;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress, &permissions);
+ if ( !image.valid() )
+ return false;
+ if ( (permissions & VM_PROT_WRITE) != 0 )
+ return false;
+ return !gAllImages.imageUnloadable(image, loadAddress);
+}
+
+
+int dladdr(const void* addr, Dl_info* info)
+{
+ log_apis("dladdr(%p, %p)\n", addr, info);
+
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+ if ( !image.valid() ) {
+ log_apis(" dladdr() => 0\n");
+ return 0;
+ }
+ MachOParser parser(loadAddress);
+ info->dli_fname = gAllImages.imagePath(image.binaryData());
+ info->dli_fbase = (void*)(loadAddress);
+ if ( addr == info->dli_fbase ) {
+ // special case lookup of header
+ info->dli_sname = "__dso_handle";
+ info->dli_saddr = info->dli_fbase;
+ }
+ else if ( parser.findClosestSymbol(addr, &(info->dli_sname), (const void**)&(info->dli_saddr)) ) {
+ // never return the mach_header symbol
+ if ( info->dli_saddr == info->dli_fbase ) {
+ info->dli_sname = nullptr;
+ info->dli_saddr = nullptr;
+ }
+ // strip off leading underscore
+ else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) {
+ info->dli_sname = info->dli_sname + 1;
+ }
+ }
+ else {
+ info->dli_sname = nullptr;
+ info->dli_saddr = nullptr;
+ }
+ log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
+ return 1;
+}
+
+
+struct PerThreadErrorMessage
+{
+ size_t sizeAllocated;
+ bool valid;
+ char message[1];
+};
+
+static pthread_key_t dlerror_perThreadKey()
+{
+ static dispatch_once_t onceToken;
+ static pthread_key_t dlerrorPThreadKey;
+ dispatch_once(&onceToken, ^{
+ pthread_key_create(&dlerrorPThreadKey, &free);
+ });
+ return dlerrorPThreadKey;
+}
+
+static void clearErrorString()
+{
+ PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+ if ( errorBuffer != nullptr )
+ errorBuffer->valid = false;
+}
+
+__attribute__((format(printf, 1, 2)))
+static void setErrorString(const char* format, ...)
+{
+ _SIMPLE_STRING buf = _simple_salloc();
+ if ( buf != nullptr ) {
+ va_list list;
+ va_start(list, format);
+ _simple_vsprintf(buf, format, list);
+ va_end(list);
+ size_t strLen = strlen(_simple_string(buf)) + 1;
+ size_t sizeNeeded = sizeof(PerThreadErrorMessage) + strLen;
+ PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+ if ( errorBuffer != nullptr ) {
+ if ( errorBuffer->sizeAllocated < sizeNeeded ) {
+ free(errorBuffer);
+ errorBuffer = nullptr;
+ }
+ }
+ if ( errorBuffer == nullptr ) {
+ size_t allocSize = std::max(sizeNeeded, (size_t)256);
+ PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize);
+ p->sizeAllocated = allocSize;
+ p->valid = false;
+ pthread_setspecific(dlerror_perThreadKey(), p);
+ errorBuffer = p;
+ }
+ strcpy(errorBuffer->message, _simple_string(buf));
+ errorBuffer->valid = true;
+ _simple_sfree(buf);
+ }
+}
+
+char* dlerror()
+{
+ log_apis("dlerror()\n");
+
+ PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+ if ( errorBuffer != nullptr ) {
+ if ( errorBuffer->valid ) {
+ // you can only call dlerror() once, then the message is cleared
+ errorBuffer->valid = false;
+ return errorBuffer->message;
+ }
+ }
+ return nullptr;
+}
+
+#if __arm64__
+ #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
+#elif __arm__
+ #define CURRENT_CPU_TYPE CPU_TYPE_ARM
+#endif
+
+
+class VIS_HIDDEN RecursiveAutoLock
+{
+public:
+ RecursiveAutoLock() {
+ pthread_mutex_lock(&_sMutex);
+ }
+ ~RecursiveAutoLock() {
+ pthread_mutex_unlock(&_sMutex);
+ }
+private:
+ static pthread_mutex_t _sMutex;
+};
+
+pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+static void* makeDlHandle(const mach_header* mh, bool dontContinue)
+{
+ uintptr_t flags = (dontContinue ? 1 : 0);
+ return (void*)((((uintptr_t)mh) >> 5) | flags);
+}
+
+VIS_HIDDEN
+void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue)
+{
+ *dontContinue = (((uintptr_t)h) & 1);
+ *mh = (const mach_header*)((((uintptr_t)h) & (-2)) << 5);
+}
+
+int dlclose(void* handle)
+{
+ log_apis("dlclose(%p)\n", handle);
+
+ // silently accept magic handles for main executable
+ if ( handle == RTLD_MAIN_ONLY )
+ return 0;
+ if ( handle == RTLD_DEFAULT )
+ return 0;
+
+ // from here on, serialize all dlopen()s
+ RecursiveAutoLock dlopenSerializer;
+
+ const mach_header* mh;
+ bool dontContinue;
+ parseDlHandle(handle, &mh, &dontContinue);
+ launch_cache::Image image = gAllImages.findByLoadAddress(mh);
+ if ( image.valid() ) {
+ // removes image if reference count went to zero
+ if ( !image.neverUnload() )
+ gAllImages.decRefCount(mh);
+ clearErrorString();
+ return 0;
+ }
+ else {
+ setErrorString("invalid handle passed to dlclose()");
+ return -1;
+ }
+}
+
+
+
+VIS_HIDDEN
+const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount)
+{
+ launch_cache::Image topImage(imageToLoad);
+ uint32_t maxLoad = topImage.maxLoadCount();
+ // first construct array of all BinImage* objects that dlopen'ed image depends on
+ const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad];
+ dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
+ imageSet.add(imageToLoad);
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+ gAllImages.copyCurrentGroups(currentGroupsList);
+ if ( !topImage.recurseAllDependentImages(currentGroupsList, imageSet, nullptr) ) {
+ diag.error("unexpected > %d images loaded", maxLoad);
+ return nullptr;
+ }
+
+ // build array of BinImage* that are not already loaded
+ const dyld3::launch_cache::binary_format::Image* toLoadImageList[maxLoad];
+ const dyld3::launch_cache::binary_format::Image** toLoadImageArray = toLoadImageList;
+ __block int needToLoadCount = 0;
+ imageSet.forEach(^(const dyld3::launch_cache::binary_format::Image* aBinImage) {
+ if ( gAllImages.findLoadAddressByImage(aBinImage) == nullptr )
+ toLoadImageArray[needToLoadCount++] = aBinImage;
+ });
+ assert(needToLoadCount > 0);
+
+ // build one array of all existing and to-be-loaded images
+ uint32_t alreadyLoadImageCount = gAllImages.count();
+ STACK_ALLOC_DYNARRAY(loader::ImageInfo, alreadyLoadImageCount + needToLoadCount, allImages);
+ loader::ImageInfo* allImagesArray = &allImages[0];
+ gAllImages.forEachImage(^(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop) {
+ launch_cache::ImageGroup grp = image.group();
+ loader::ImageInfo& info= allImagesArray[imageIndex];
+ info.imageData = image.binaryData();
+ info.loadAddress = loadAddress;
+ info.groupNum = grp.groupNum();
+ info.indexInGroup = grp.indexInGroup(info.imageData);
+ info.previouslyFixedUp = true;
+ info.justMapped = false;
+ info.justUsedFromDyldCache = false;
+ info.neverUnload = false;
+ });
+ for (int i=0; i < needToLoadCount; ++i) {
+ launch_cache::Image img(toLoadImageArray[i]);
+ launch_cache::ImageGroup grp = img.group();
+ loader::ImageInfo& info= allImages[alreadyLoadImageCount+i];
+ info.imageData = toLoadImageArray[i];
+ info.loadAddress = nullptr;
+ info.groupNum = grp.groupNum();
+ info.indexInGroup = grp.indexInGroup(img.binaryData());
+ info.previouslyFixedUp = false;
+ info.justMapped = false;
+ info.justUsedFromDyldCache = false;
+ info.neverUnload = false;
+ }
+
+ // map new images and apply all fixups
+ mapAndFixupImages(diag, allImages, (const uint8_t*)gAllImages.cacheLoadAddress(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
+ if ( diag.hasError() )
+ return nullptr;
+ const mach_header* topLoadAddress = allImages[alreadyLoadImageCount].loadAddress;
+
+ // bump dlopen refcount of image directly loaded
+ if ( bumpDlopenCount )
+ gAllImages.incRefCount(topLoadAddress);
+
+ // tell gAllImages about new images
+ dyld3::launch_cache::DynArray<loader::ImageInfo> newImages(needToLoadCount, &allImages[alreadyLoadImageCount]);
+ gAllImages.addImages(newImages);
+
+ // tell gAllImages about any old images which now have never unload set
+ for (int i=0; i < alreadyLoadImageCount; ++i) {
+ if (allImages[i].neverUnload && !allImages[i].imageData->neverUnload)
+ gAllImages.setNeverUnload(allImages[i]);
+ }
+
+ // run initializers
+ gAllImages.runInitialzersBottomUp(topLoadAddress);
+
+ return topLoadAddress;
+}
+
+
+void* dlopen(const char* path, int mode)
+{
+ log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
+
+ clearErrorString();
+
+ // 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
+ if ( (mode & RTLD_FIRST) != 0 )
+ return RTLD_MAIN_ONLY;
+ else
+ return RTLD_DEFAULT;
+ }
+
+ // from here on, serialize all dlopen()s
+ RecursiveAutoLock dlopenSerializer;
+
+ const char* leafName = strrchr(path, '/');
+ if ( leafName != nullptr )
+ ++leafName;
+ else
+ leafName = path;
+
+ // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
+ bool dontContinue = (mode & RTLD_FIRST);
+ bool bumpRefCount = true;
+
+ // check if dylib with same inode/mtime is already loaded
+ __block const mach_header* alreadyLoadMH = nullptr;
+ struct stat statBuf;
+ if ( stat(path, &statBuf) == 0 ) {
+ alreadyLoadMH = gAllImages.alreadyLoaded(statBuf.st_ino, statBuf.st_mtime, bumpRefCount);
+ if ( alreadyLoadMH != nullptr) {
+ log_apis(" dlopen: path inode/mtime matches already loaded image\n");
+ void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+ }
+
+ // check if already loaded, and if so, just bump ref-count
+ gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
+ alreadyLoadMH = gAllImages.alreadyLoaded(possiblePath, bumpRefCount);
+ if ( alreadyLoadMH != nullptr ) {
+ log_apis(" dlopen: matches already loaded image %s\n", possiblePath);
+ stop = true;
+ }
+ });
+ if ( alreadyLoadMH != nullptr) {
+ void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+
+ // it may be that the path supplied is a symlink to something already loaded
+ char resolvedPath[PATH_MAX];
+ const char* realPathResult = realpath(path, resolvedPath);
+ // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+ bool checkRealPathToo = ((realPathResult != nullptr) || (errno == ENOENT)) && (strcmp(path, resolvedPath) != 0);
+ if ( checkRealPathToo ) {
+ alreadyLoadMH = gAllImages.alreadyLoaded(resolvedPath, bumpRefCount);
+ log_apis(" dlopen: real path=%s\n", resolvedPath);
+ if ( alreadyLoadMH != nullptr) {
+ void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+ }
+
+ // check if image is in a known ImageGroup
+ __block const launch_cache::binary_format::Image* imageToLoad = nullptr;
+ gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
+ log_apis(" dlopen: checking for pre-built closure for path: %s\n", possiblePath);
+ imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
+ if ( imageToLoad != nullptr )
+ stop = true;
+ });
+ if ( (imageToLoad == nullptr) && checkRealPathToo ) {
+ gPathOverrides.forEachPathVariant(resolvedPath, ^(const char* possiblePath, bool& stop) {
+ log_apis(" dlopen: checking for pre-built closure for real path: %s\n", possiblePath);
+ imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
+ if ( imageToLoad != nullptr )
+ stop = true;
+ });
+ }
+
+ // check if image from a known ImageGroup is already loaded (via a different path)
+ if ( imageToLoad != nullptr ) {
+ alreadyLoadMH = gAllImages.alreadyLoaded(imageToLoad, bumpRefCount);
+ if ( alreadyLoadMH != nullptr) {
+ void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+ }
+
+ // RTLD_NOLOAD means do nothing if image not already loaded
+ if ( mode & RTLD_NOLOAD ) {
+ log_apis(" dlopen(%s) => NULL\n", leafName);
+ return nullptr;
+ }
+
+ // if we have a closure, optimistically use it. If out of date, it will fail
+ if ( imageToLoad != nullptr ) {
+ log_apis(" dlopen: trying existing closure image=%p\n", imageToLoad);
+ Diagnostics diag;
+ const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
+ if ( diag.noError() ) {
+ void* result = makeDlHandle(topLoadAddress, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+ // image is no longer valid, will need to build one
+ imageToLoad = nullptr;
+ log_apis(" dlopen: existing closure no longer valid\n");
+ }
+
+ // if no existing closure, RPC to closured to create one
+ const char* closuredErrorMessages[3];
+ int closuredErrorMessagesCount = 0;
+ if ( imageToLoad == nullptr ) {
+ imageToLoad = gAllImages.messageClosured(path, "dlopen", closuredErrorMessages, closuredErrorMessagesCount);
+ }
+
+ // load images using new closure
+ if ( imageToLoad != nullptr ) {
+ log_apis(" dlopen: using closured built image=%p\n", imageToLoad);
+ Diagnostics diag;
+ const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
+ if ( diag.noError() ) {
+ void* result = makeDlHandle(topLoadAddress, dontContinue);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
+ }
+ if ( closuredErrorMessagesCount < 3 ) {
+ closuredErrorMessages[closuredErrorMessagesCount++] = strdup(diag.errorMessage());
+ }
+ }
+
+ // otherwise, closured failed to build needed load info
+ switch ( closuredErrorMessagesCount ) {
+ case 0:
+ setErrorString("dlopen(%s, 0x%04X): closured error", path, mode);
+ log_apis(" dlopen: closured error\n");
+ break;
+ case 1:
+ setErrorString("dlopen(%s, 0x%04X): %s", path, mode, closuredErrorMessages[0]);
+ log_apis(" dlopen: closured error: %s\n", closuredErrorMessages[0]);
+ break;
+ case 2:
+ setErrorString("dlopen(%s, 0x%04X): %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1]);
+ log_apis(" dlopen: closured error: %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1]);
+ break;
+ case 3:
+ setErrorString("dlopen(%s, 0x%04X): %s %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
+ log_apis(" dlopen: closured error: %s %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
+ break;
+ }
+ for (int i=0; i < closuredErrorMessagesCount;++i)
+ free((void*)closuredErrorMessages[i]);
+
+ log_apis(" dlopen(%s) => NULL\n", leafName);
+
+ return nullptr;
+}
+
+bool dlopen_preflight(const char* path)
+{
+ log_apis("dlopen_preflight(%s)\n", path);
+
+ if ( gAllImages.alreadyLoaded(path, false) != nullptr )
+ return true;
+
+ if ( gAllImages.findImageInKnownGroups(path) != nullptr )
+ return true;
+
+ // map whole file
+ struct stat statBuf;
+ if ( ::stat(path, &statBuf) != 0 )
+ return false;
+ int fd = ::open(path, O_RDONLY);
+ if ( fd < 0 )
+ return false;
+ const void* fileBuffer = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ ::close(fd);
+ if ( fileBuffer == MAP_FAILED )
+ return false;
+ size_t mappedSize = (size_t)statBuf.st_size;
+
+ // check if it is current arch mach-o or fat with slice for current arch
+ __block bool result = false;
+ __block Diagnostics diag;
+ if ( MachOParser::isMachO(diag, fileBuffer, mappedSize) ) {
+ result = true;
+ }
+ else {
+ if ( FatUtil::isFatFile(fileBuffer) ) {
+ FatUtil::forEachSlice(diag, fileBuffer, mappedSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSz, bool& stop) {
+ if ( MachOParser::isMachO(diag, sliceStart, sliceSz) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ }
+ }
+ ::munmap((void*)fileBuffer, mappedSize);
+
+ // FIXME: may be symlink to something in dyld cache
+
+ // FIXME: maybe ask closured
+
+ return result;
+}
+
+static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower)
+{
+ // construct array of all BinImage* objects that dlopen'ed image depends on
+ uint32_t maxLoad = startImage.maxLoadCount();
+ const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad];
+ dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
+ imageSet.add(startImage.binaryData());
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+ gAllImages.copyCurrentGroups(currentGroupsList);
+
+ __block void* result = nullptr;
+ auto handler = ^(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop) {
+ const mach_header* loadAddress = gAllImages.findLoadAddressByImage(aBinImage);
+ if ( !searchStartImage && (loadAddress == startImageLoadAddress) )
+ return;
+ if ( loadAddress != nullptr ) {
+ MachOParser parser(loadAddress);
+ if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) {
+ stop = true;
+ }
+ }
+ };
+
+ bool stop = false;
+ handler(startImage.binaryData(), stop);
+ if (stop)
+ return result;
+
+ // check each dependent image for symbol
+ if ( !startImage.recurseAllDependentImages(currentGroupsList, imageSet, handler) ) {
+ setErrorString("unexpected > %d images loaded", maxLoad);
+ return nullptr;
+ }
+ return result;
+}
+
+void* dlsym(void* handle, const char* symbolName)
+{
+ log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
+
+ clearErrorString();
+
+ // dlsym() assumes symbolName passed in is same as in C source code
+ // dyld assumes all symbol names have an underscore prefix
+ char underscoredName[strlen(symbolName)+2];
+ underscoredName[0] = '_';
+ strcpy(&underscoredName[1], symbolName);
+
+ // this block is only used if hasExportedSymbol() needs to trace re-exported dylibs to find a symbol
+ MachOParser::DependentFinder reExportFollower = ^(uint32_t targetDepIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ if ( (strncmp(depLoadPath, "@rpath/", 7) == 0) && (extra != nullptr) ) {
+ const mach_header* parentMH = (mach_header*)extra;
+ launch_cache::Image parentImage = gAllImages.findByLoadAddress(parentMH);
+ if ( parentImage.valid() ) {
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+ gAllImages.copyCurrentGroups(currentGroupsList);
+ parentImage.forEachDependentImage(currentGroupsList, ^(uint32_t parentDepIndex, dyld3::launch_cache::Image parentDepImage, dyld3::launch_cache::Image::LinkKind kind, bool &stop) {
+ if ( parentDepIndex != targetDepIndex )
+ return;
+ const mach_header* parentDepMH = gAllImages.findLoadAddressByImage(parentDepImage.binaryData());
+ if ( parentDepMH != nullptr ) {
+ *foundMH = parentDepMH;
+ stop = true;
+ }
+ });
+ }
+ }
+ else {
+ *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+ }
+ return (*foundMH != nullptr);
+ };
+
+ if ( handle == RTLD_DEFAULT ) {
+ // magic "search all in load order" handle
+ for (uint32_t index=0; index < gAllImages.count(); ++index) {
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress);
+ if ( image.valid() ) {
+ MachOParser parser(loadAddress);
+ void* result;
+ //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
+ if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
+ log_apis(" dlsym() => %p\n", result);
+ return result;
+ }
+ }
+ }
+ setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
+ log_apis(" dlsym() => NULL\n");
+ return nullptr;
+ }
+ else if ( handle == RTLD_MAIN_ONLY ) {
+ // magic "search only main executable" handle
+ MachOParser parser(gAllImages.mainExecutable());
+ //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
+ void* result;
+ if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
+ log_apis(" dlsym() => %p\n", result);
+ return result;
+ }
+ setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
+ log_apis(" dlsym() => NULL\n");
+ return nullptr;
+ }
+
+ // rest of cases search in dependency order
+ const mach_header* startImageLoadAddress;
+ launch_cache::Image startImage(nullptr);
+ void* result = nullptr;
+ if ( handle == RTLD_NEXT ) {
+ // magic "search what I would see" handle
+ void* callerAddress = __builtin_return_address(0);
+ startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
+ if ( ! startImage.valid() ) {
+ setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
+ return nullptr;
+ }
+ result = dlsym_search(underscoredName, startImageLoadAddress, startImage, false, reExportFollower);
+ }
+ else if ( handle == RTLD_SELF ) {
+ // magic "search me, then what I would see" handle
+ void* callerAddress = __builtin_return_address(0);
+ startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
+ if ( ! startImage.valid() ) {
+ setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
+ return nullptr;
+ }
+ result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
+ }
+ else {
+ // handle value was something returned by dlopen()
+ bool dontContinue;
+ parseDlHandle(handle, &startImageLoadAddress, &dontContinue);
+ startImage = gAllImages.findByLoadAddress(startImageLoadAddress);
+ if ( !startImage.valid() ) {
+ setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName);
+ log_apis(" dlsym() => NULL\n");
+ return nullptr;
+ }
+ if ( dontContinue ) {
+ // RTLD_FIRST only searches one place
+ MachOParser parser(startImageLoadAddress);
+ parser.hasExportedSymbol(underscoredName, reExportFollower, &result);
+ }
+ else {
+ result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
+ }
+ }
+
+ if ( result != nullptr ) {
+ log_apis(" dlsym() => %p\n", result);
+ return result;
+ }
+
+ setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName);
+ log_apis(" dlsym() => NULL\n");
+ return nullptr;
+}
+
+
+const struct dyld_all_image_infos* _dyld_get_all_image_infos()
+{
+ return gAllImages.oldAllImageInfo();
+}
+
+bool dyld_shared_cache_some_image_overridden()
+{
+ log_apis("dyld_shared_cache_some_image_overridden()\n");
+
+ assert(0 && "not implemented yet");
+}
+
+bool _dyld_get_shared_cache_uuid(uuid_t uuid)
+{
+ log_apis("_dyld_get_shared_cache_uuid()\n");
+
+ if ( gAllImages.oldAllImageInfo() != nullptr ) {
+ memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t));
+ return true;
+ }
+ return false;
+}
+
+const void* _dyld_get_shared_cache_range(size_t* mappedSize)
+{
+ log_apis("_dyld_get_shared_cache_range()\n");
+
+ const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+ if ( sharedCache != nullptr ) {
+ *mappedSize = (size_t)sharedCache->mappedSize();
+ return sharedCache;
+ }
+ *mappedSize = 0;
+ return NULL;
+}
+
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+{
+ log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
+
+ const mach_header* mh = dyld_image_header_containing_address(addr);
+ if ( mh == nullptr )
+ return false;
+
+ info->mh = mh;
+ info->dwarf_section = nullptr;
+ info->dwarf_section_length = 0;
+ info->compact_unwind_section = nullptr;
+ info->compact_unwind_section_length = 0;
+
+ MachOParser parser(mh);
+ parser.forEachSection(^(const char* segName, const char* sectName, uint32_t flags, const void* content, size_t sectSize, bool illegalSectionSize, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ if ( strcmp(sectName, "__eh_frame") == 0 ) {
+ info->dwarf_section = content;
+ info->dwarf_section_length = sectSize;
+ }
+ else if ( strcmp(sectName, "__unwind_info") == 0 ) {
+ info->compact_unwind_section = content;
+ info->compact_unwind_section_length = sectSize;
+ }
+ }
+ });
+
+ return true;
+}
+
+
+bool dyld_process_is_restricted()
+{
+ log_apis("dyld_process_is_restricted()\n");
+
+ launch_cache::Closure closure(gAllImages.mainClosure());
+ return closure.isRestricted();
+}
+
+
+const char* dyld_shared_cache_file_path()
+{
+ log_apis("dyld_shared_cache_file_path()\n");
+
+ return gAllImages.dyldCachePath();
+}
+
+
+void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count)
+{
+ log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count);
+ // FIXME
+}
+
+
+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 DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped)
+{
+ 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 DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
+ uuid_t foundUuid;
+ cache->getUUID(foundUuid);
+ if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
+ // wrong uuid, unmap and keep looking
+ ::munmap((void*)cache, 0x00100000);
+ }
+ else {
+ // found cache
+ closedir(dirp);
+ sizeMapped = 0x00100000;
+ return cache;
+ }
+ }
+ }
+ closedir(dirp);
+ }
+ return nullptr;
+}
+
+int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+ log_apis("dyld_shared_cache_find_iterate_text()\n");
+
+ // see if requested cache is the active one in this process
+ size_t sizeMapped = 0;
+ const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+ if ( sharedCache != nullptr ) {
+ uuid_t runningUuid;
+ sharedCache->getUUID(runningUuid);
+ if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 )
+ sharedCache = nullptr;
+ }
+ if ( sharedCache == nullptr ) {
+ // if not, look in 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
+ sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped);
+ // if not there, look in extra search locations
+ if ( sharedCache == nullptr ) {
+ for (const char** p = extraSearchDirs; *p != nullptr; ++p) {
+ sharedCache = findCacheInDirAndMap(cacheUuid, *p, sizeMapped);
+ if ( sharedCache != nullptr )
+ break;
+ }
+ }
+ }
+ if ( sharedCache == nullptr )
+ return -1;
+
+ // get base address of cache
+ __block uint64_t cacheUnslidBaseAddress = 0;
+ sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if ( cacheUnslidBaseAddress == 0 )
+ cacheUnslidBaseAddress = vmAddr;
+ });
+
+ // iterate all images
+ sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) {
+ dyld_shared_cache_dylib_text_info dylibTextInfo;
+ dylibTextInfo.version = 2;
+ dylibTextInfo.loadAddressUnslid = loadAddressUnslid;
+ dylibTextInfo.textSegmentSize = textSegmentSize;
+ dylibTextInfo.path = installName;
+ ::memcpy(dylibTextInfo.dylibUuid, dylibUUID, 16);
+ dylibTextInfo.textSegmentOffset = loadAddressUnslid - cacheUnslidBaseAddress;
+ callback(&dylibTextInfo);
+ });
+
+ if ( sizeMapped != 0 )
+ ::munmap((void*)sharedCache, sizeMapped);
+
+ return 0;
+}
+
+int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+ log_apis("dyld_shared_cache_iterate_text()\n");
+
+ const char* extraSearchDirs[] = { NULL };
+ return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
+}
+
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_APIS_H__
+#define __DYLD_APIS_H__
+
+#include <string.h>
+#include <stdint.h>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+
+#define TEMP_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+
+uint32_t _dyld_image_count() TEMP_HIDDEN;
+
+const mach_header* _dyld_get_image_header(uint32_t imageIndex) TEMP_HIDDEN;
+
+intptr_t _dyld_get_image_slide(const mach_header* mh) TEMP_HIDDEN;
+
+intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) TEMP_HIDDEN;
+
+const char* _dyld_get_image_name(uint32_t imageIndex) TEMP_HIDDEN;
+
+int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) TEMP_HIDDEN;
+
+int32_t NSVersionOfRunTimeLibrary(const char* libraryName) TEMP_HIDDEN;
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+uint32_t dyld_get_program_sdk_watch_os_version() TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_watch_os_version() TEMP_HIDDEN;
+#endif
+
+#if TARGET_OS_BRIDGE
+uint32_t dyld_get_program_sdk_bridge_os_version() TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_bridge_os_version() TEMP_HIDDEN;
+#endif
+
+
+uint32_t dyld_get_sdk_version(const mach_header* mh) TEMP_HIDDEN;
+
+
+uint32_t dyld_get_program_sdk_version() TEMP_HIDDEN;
+uint32_t dyld_get_min_os_version(const mach_header* mh) TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_os_version() TEMP_HIDDEN;
+
+
+bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) TEMP_HIDDEN;
+
+int _NSGetExecutablePath(char* buf, uint32_t* bufsize) TEMP_HIDDEN;
+
+void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN;
+
+void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN;
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
+ _dyld_objc_notify_init init,
+ _dyld_objc_notify_unmapped unmapped) TEMP_HIDDEN;
+
+const mach_header* dyld_image_header_containing_address(const void* addr) TEMP_HIDDEN;
+
+const mach_header* _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN;
+
+bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN;
+
+const char* dyld_image_path_containing_address(const void* addr) TEMP_HIDDEN;
+
+bool _dyld_is_memory_immutable(const void* addr, size_t length) TEMP_HIDDEN;
+
+
+int dladdr(const void* addr, Dl_info* info) TEMP_HIDDEN;
+
+char* dlerror() TEMP_HIDDEN;
+
+int dlclose(void* handle) TEMP_HIDDEN;
+
+void* dlopen(const char* path, int mode) TEMP_HIDDEN;
+
+bool dlopen_preflight(const char* path) TEMP_HIDDEN;
+
+void* dlsym(void* handle, const char* symbolName) TEMP_HIDDEN;
+
+const struct dyld_all_image_infos* _dyld_get_all_image_infos() TEMP_HIDDEN;
+
+bool dyld_shared_cache_some_image_overridden() TEMP_HIDDEN;
+
+bool _dyld_get_shared_cache_uuid(uuid_t uuid) TEMP_HIDDEN;
+
+const void* _dyld_get_shared_cache_range(size_t* length) TEMP_HIDDEN;
+
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) TEMP_HIDDEN;
+
+bool dyld_process_is_restricted() TEMP_HIDDEN;
+
+const char* dyld_shared_cache_file_path() TEMP_HIDDEN;
+
+void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count) TEMP_HIDDEN;
+
+int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN;
+
+int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN;
+
+void _dyld_fork_child() TEMP_HIDDEN;
+
+// only in macOS and deprecated
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
+bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) TEMP_HIDDEN;
+uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) TEMP_HIDDEN;
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) TEMP_HIDDEN;
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) TEMP_HIDDEN;
+const char* NSNameOfModule(NSModule m) TEMP_HIDDEN;
+const char* NSLibraryNameForModule(NSModule m) TEMP_HIDDEN;
+NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) TEMP_HIDDEN;
+bool NSUnLinkModule(NSModule module, uint32_t options) TEMP_HIDDEN;
+bool NSIsSymbolNameDefined(const char* symbolName) TEMP_HIDDEN;
+bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN;
+bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupAndBindSymbol(const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN;
+NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) TEMP_HIDDEN;
+const char* NSNameOfSymbol(NSSymbol symbol) TEMP_HIDDEN;
+void* NSAddressOfSymbol(NSSymbol symbol) TEMP_HIDDEN;
+NSModule NSModuleForSymbol(NSSymbol symbol) TEMP_HIDDEN;
+void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) TEMP_HIDDEN;
+bool NSAddLibrary(const char* pathName) TEMP_HIDDEN;
+bool NSAddLibraryWithSearching(const char* pathName) TEMP_HIDDEN;
+const struct mach_header* NSAddImage(const char* image_name, uint32_t options) TEMP_HIDDEN;
+void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) TEMP_HIDDEN;
+bool _dyld_present(void) TEMP_HIDDEN;
+bool _dyld_launched_prebound(void) TEMP_HIDDEN;
+bool _dyld_all_twolevel_modules_prebound(void) TEMP_HIDDEN;
+bool _dyld_bind_fully_image_containing_address(const void* address) TEMP_HIDDEN;
+bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN;
+void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) TEMP_HIDDEN;
+void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) TEMP_HIDDEN;
+void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) TEMP_HIDDEN;
+const struct mach_header* _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN;
+#endif
+
+} // namespace dyld3
+
+#endif // __DYLD_APIS_H__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <_simple.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <TargetConditionals.h>
+#include <malloc/malloc.h>
+
+#include <algorithm>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "Logging.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "APIs.h"
+
+
+
+typedef dyld3::launch_cache::binary_format::Image BinaryImage;
+
+
+namespace dyld3 {
+
+// from APIs.cpp
+void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue);
+const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount);
+
+
+// only in macOS and deprecated
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+
+// macOS needs to support an old API that only works with fileype==MH_BUNDLE.
+// In this deprecated API (unlike dlopen), loading and linking are separate steps.
+// NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file.
+// NSLinkModule() does the load of dependent modules and rebasing/binding.
+// To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order!
+//
+
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NSObjectFileImage* ofi)
+{
+ log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path, ofi);
+
+ // verify path exists
+ struct stat statbuf;
+ if ( ::stat(path, &statbuf) == -1 )
+ return NSObjectFileImageFailure;
+
+ // create ofi that just contains path. NSLinkModule does all the work
+ __NSObjectFileImage* result = gAllImages.addNSObjectFileImage();
+ result->path = strdup(path);
+ result->memSource = nullptr;
+ result->memLength = 0;
+ result->loadAddress = nullptr;
+ result->binImage = nullptr;
+ *ofi = result;
+
+ log_apis("NSCreateObjectFileImageFromFile() => %p\n", result);
+
+ return NSObjectFileImageSuccess;
+}
+
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memImage, size_t memImageSize, NSObjectFileImage *ofi)
+{
+ log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage, memImageSize, ofi);
+
+ // sanity check the buffer is a mach-o file
+ __block Diagnostics diag;
+ __block const mach_header* foundMH = nullptr;
+ if ( MachOParser::isMachO(diag, memImage, memImageSize) ) {
+ foundMH = (mach_header*)memImage;
+ }
+ else {
+ FatUtil::forEachSlice(diag, memImage, memImageSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+ if ( MachOParser::isMachO(diag, sliceStart, sliceSize) ) {
+ foundMH = (mach_header*)sliceStart;
+ stop = true;
+ }
+ });
+ }
+ if ( foundMH == nullptr ) {
+ log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
+ return NSObjectFileImageFailure;
+ }
+
+ // this API can only be used with bundles
+ if ( foundMH->filetype != MH_BUNDLE ) {
+ log_apis("NSCreateObjectFileImageFromMemory() not a bundle, filetype=%d\n", foundMH->filetype);
+ return NSObjectFileImageInappropriateFile;
+ }
+
+ // allocate ofi that just lists the memory range
+ __NSObjectFileImage* result = gAllImages.addNSObjectFileImage();
+ result->path = nullptr;
+ result->memSource = memImage;
+ result->memLength = memImageSize;
+ result->loadAddress = nullptr;
+ result->binImage = nullptr;
+ *ofi = result;
+
+ log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result);
+
+ return NSObjectFileImageSuccess;
+}
+
+NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
+{
+ log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi, moduleName, options);
+
+ // ofi is invalid if not in list
+ if ( !gAllImages.hasNSObjectFileImage(ofi) ) {
+ log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
+ return nullptr;
+ }
+
+ // if this is memory based image, write to temp file, then use file based loading
+ const BinaryImage* imageToLoad = nullptr;
+ if ( ofi->memSource != nullptr ) {
+ // make temp file with content of memory buffer
+ bool successfullyWritten = false;
+ ofi->path = ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-");
+ if ( ofi->path != nullptr ) {
+ int fd = ::open(ofi->path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if ( fd != -1 ) {
+ ssize_t writtenSize = ::pwrite(fd, ofi->memSource, ofi->memLength, 0);
+ if ( writtenSize == ofi->memLength )
+ successfullyWritten = true;
+ ::close(fd);
+ }
+ }
+ if ( !successfullyWritten ) {
+ if ( ofi->path != nullptr ) {
+ free((void*)ofi->path);
+ ofi->path = nullptr;
+ }
+ log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
+ return nullptr;
+ }
+ }
+ else {
+ // check if image is in a known ImageGroup, but not loaded. if so, load using existing closure info
+ log_apis(" NSLinkModule: checking for pre-built closure for path: %s\n", ofi->path);
+ imageToLoad = gAllImages.findImageInKnownGroups(ofi->path);
+ // TODO: check symlinks, realpath
+ }
+
+ // if no existing closure, RPC to closured to create one
+ if ( imageToLoad == nullptr ) {
+ const char* closuredErrorMessages[3];
+ int closuredErrorMessagesCount = 0;
+ if ( imageToLoad == nullptr ) {
+ imageToLoad = gAllImages.messageClosured(ofi->path, "NSLinkModule", closuredErrorMessages, closuredErrorMessagesCount);
+ }
+ for (int i=0; i < closuredErrorMessagesCount; ++i) {
+ log_apis(" NSLinkModule: failed: %s\n", closuredErrorMessages[i]);
+ free((void*)closuredErrorMessages[i]);
+ }
+ }
+
+ // use Image info to load and fixup image and all its dependents
+ if ( imageToLoad != nullptr ) {
+ Diagnostics diag;
+ ofi->loadAddress = loadImageAndDependents(diag, imageToLoad, true);
+ if ( diag.hasError() )
+ log_apis(" NSLinkModule: failed: %s\n", diag.errorMessage());
+ }
+
+ // if memory based load, delete temp file
+ if ( ofi->memSource != nullptr ) {
+ log_apis(" NSLinkModule: delete temp file: %s\n", ofi->path);
+ ::unlink(ofi->path);
+ }
+
+ log_apis("NSLinkModule() => %p\n", ofi->loadAddress);
+ return (NSModule)ofi->loadAddress;
+}
+
+// NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
+bool NSUnLinkModule(NSModule module, uint32_t options)
+{
+ log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options);
+
+ bool result = false;
+ const mach_header* mh = (mach_header*)module;
+ launch_cache::Image image = gAllImages.findByLoadAddress(mh);
+ if ( image.valid() ) {
+ // removes image if reference count went to zero
+ gAllImages.decRefCount(mh);
+ result = true;
+ }
+
+ log_apis("NSUnLinkModule() => %d\n", result);
+
+ return result;
+}
+
+// NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
+bool NSDestroyObjectFileImage(NSObjectFileImage ofi)
+{
+ log_apis("NSDestroyObjectFileImage(%p)\n", ofi);
+
+ // ofi is invalid if not in list
+ if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ return false;
+
+ // keep copy of info
+ const void* memSource = ofi->memSource;
+ size_t memLength = ofi->memLength;
+ const char* path = ofi->path;
+
+ // remove from list
+ gAllImages.removeNSObjectFileImage(ofi);
+
+ // if object was created from a memory, release that memory
+ // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld
+ if ( memSource != nullptr ) {
+ // we don't know if memory came from malloc or vm_allocate, so ask malloc
+ if ( malloc_size(memSource) != 0 )
+ free((void*)(memSource));
+ else
+ vm_deallocate(mach_task_self(), (vm_address_t)memSource, memLength);
+ }
+ free((void*)path);
+
+ return true;
+}
+
+uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)
+{
+ halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
+}
+
+const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)
+{
+ halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
+}
+
+uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)
+{
+ halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
+}
+
+const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition)
+{
+ halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
+}
+
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi, const char* symbolName)
+{
+ log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi, symbolName);
+
+ // ofi is invalid if not in list
+ if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ return false;
+
+ void* addr;
+ MachOParser parser(ofi->loadAddress);
+ return parser.hasExportedSymbol(symbolName, ^(uint32_t , const char*, void*, const mach_header**, void**) {
+ return false;
+ }, &addr);
+}
+
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi, const char* segmentName, const char* sectionName, size_t* size)
+{
+ // ofi is invalid if not in list
+ if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ return nullptr;
+
+ __block void* result = nullptr;
+ MachOParser parser(ofi->loadAddress);
+ parser.forEachSection(^(const char* aSegName, const char* aSectName, uint32_t flags, const void* content, size_t aSize, bool illegalSectionSize, bool& stop) {
+ if ( (strcmp(sectionName, aSectName) == 0) && (strcmp(segmentName, aSegName) == 0) ) {
+ result = (void*)content;
+ if ( size != nullptr )
+ *size = aSize;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+const char* NSNameOfModule(NSModule m)
+{
+ log_apis("NSNameOfModule(%p)\n", m);
+
+ const mach_header* foundInLoadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress);
+ if ( image.valid() ) {
+ return gAllImages.imagePath(image.binaryData());
+ }
+ return nullptr;
+}
+
+const char* NSLibraryNameForModule(NSModule m)
+{
+ log_apis("NSLibraryNameForModule(%p)\n", m);
+
+ const mach_header* foundInLoadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress);
+ if ( image.valid() ) {
+ return gAllImages.imagePath(image.binaryData());
+ }
+ return nullptr;
+}
+
+
+static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress)
+{
+ for (uint32_t index=0; index < gAllImages.count(); ++index) {
+ const mach_header* loadAddress;
+ launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress);
+ if ( image.valid() ) {
+ MachOParser parser(loadAddress);
+ if ( parser.hasExportedSymbol(symbolName, ^(uint32_t , const char* , void* , const mach_header** , void**) { return false; }, symbolAddress) ) {
+ *foundInImageAtLoadAddress = loadAddress;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool NSIsSymbolNameDefined(const char* symbolName)
+{
+ log_apis("NSIsSymbolNameDefined(%s)\n", symbolName);
+
+ const mach_header* foundInImageAtLoadAddress;
+ void* address;
+ return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
+}
+
+bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint)
+{
+ log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName, libraryNameHint);
+
+ const mach_header* foundInImageAtLoadAddress;
+ void* address;
+ return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
+}
+
+bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName)
+{
+ log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh, symbolName);
+
+ MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+ return (*foundMH != nullptr);
+ };
+
+ MachOParser parser(mh);
+ void* result;
+ return parser.hasExportedSymbol(symbolName, reExportFollower, &result);
+}
+
+NSSymbol NSLookupAndBindSymbol(const char* symbolName)
+{
+ log_apis("NSLookupAndBindSymbol(%s)\n", symbolName);
+
+ const mach_header* foundInImageAtLoadAddress;
+ void* symbolAddress;
+ if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
+ return (NSSymbol)symbolAddress;
+ }
+ return nullptr;
+}
+
+NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint)
+{
+ log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName, libraryNameHint);
+
+ const mach_header* foundInImageAtLoadAddress;
+ void* symbolAddress;
+ if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
+ return (NSSymbol)symbolAddress;
+ }
+ return nullptr;
+}
+
+NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName)
+{
+ log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName);
+
+ MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+ return (*foundMH != nullptr);
+ };
+
+ const mach_header* mh = (const mach_header*)module;
+ uint32_t loadIndex;
+ if ( gAllImages.findIndexForLoadAddress(mh, loadIndex) ) {
+ MachOParser parser(mh);
+ void* symAddress;
+ if ( parser.hasExportedSymbol(symbolName, reExportFollower, &symAddress) ) {
+ return (NSSymbol)symAddress;
+ }
+ }
+ return nullptr;
+}
+
+NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options)
+{
+ log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh, symbolName, options);
+
+ MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+ return (*foundMH != nullptr);
+ };
+
+ MachOParser parser(mh);
+ void* result;
+ if ( parser.hasExportedSymbol(symbolName, reExportFollower, &result) ) {
+ log_apis(" NSLookupSymbolInImage() => %p\n", result);
+ return (NSSymbol)result;
+ }
+
+ if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
+ log_apis(" NSLookupSymbolInImage() => NULL\n");
+ return nullptr;
+ }
+ return nullptr;
+}
+
+const char* NSNameOfSymbol(NSSymbol symbol)
+{
+ halt("NSNameOfSymbol() is obsolete");
+}
+
+void* NSAddressOfSymbol(NSSymbol symbol)
+{
+ log_apis("NSAddressOfSymbol(%p)\n", symbol);
+
+ // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table
+ return (void*)symbol;
+}
+
+NSModule NSModuleForSymbol(NSSymbol symbol)
+{
+ log_apis("NSModuleForSymbol(%p)\n", symbol);
+
+ const mach_header* foundInLoadAddress;
+ launch_cache::Image image = gAllImages.findByOwnedAddress(symbol, &foundInLoadAddress);
+ if ( image.valid() ) {
+ return (NSModule)foundInLoadAddress;
+ }
+ return nullptr;
+}
+
+void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString)
+{
+ log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c, errorNumber, fileName, errorString);
+ *c = NSLinkEditOtherError;
+ *errorNumber = 0;
+ *fileName = NULL;
+ *errorString = NULL;
+}
+
+bool NSAddLibrary(const char* pathName)
+{
+ log_apis("NSAddLibrary(%s)\n", pathName);
+
+ return ( dlopen(pathName, 0) != nullptr);
+}
+
+bool NSAddLibraryWithSearching(const char* pathName)
+{
+ log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
+
+ return ( dlopen(pathName, 0) != nullptr);
+}
+
+const mach_header* NSAddImage(const char* imageName, uint32_t options)
+{
+ log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName, options);
+
+ // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags
+ uint32_t dloptions = 0;
+ if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 )
+ dloptions |= RTLD_NOLOAD;
+
+ void* h = dlopen(imageName, dloptions);
+ if ( h != nullptr ) {
+ const mach_header* mh;
+ bool dontContinue;
+ parseDlHandle(h, &mh, &dontContinue);
+ return mh;
+ }
+
+ if ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ) {
+ halt("NSAddImage() image not found");
+ }
+ return nullptr;
+}
+
+void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers)
+{
+ halt("NSInstallLinkEditErrorHandlers() is obsolete");
+}
+
+bool _dyld_present(void)
+{
+ log_apis("_dyld_present()\n");
+
+ return true;
+}
+
+bool _dyld_launched_prebound(void)
+{
+ halt("_dyld_launched_prebound() is obsolete");
+}
+
+bool _dyld_all_twolevel_modules_prebound(void)
+{
+ halt("_dyld_all_twolevel_modules_prebound() is obsolete");
+}
+
+bool _dyld_bind_fully_image_containing_address(const void* address)
+{
+ log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address);
+
+ // in dyld3, everything is always fully bound
+ return true;
+}
+
+bool _dyld_image_containing_address(const void* address)
+{
+ log_apis("_dyld_image_containing_address(%p)\n", address);
+
+ return (dyld_image_header_containing_address(address) != nullptr);
+}
+
+void _dyld_lookup_and_bind(const char* symbolName, void **address, NSModule* module)
+{
+ log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName, address, module);
+
+ const mach_header* foundInImageAtLoadAddress;
+ if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+ *module = (NSModule)foundInImageAtLoadAddress;
+ return;
+ }
+
+ *address = 0;
+ *module = 0;
+}
+
+void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* libraryNameHint, void** address, NSModule* module)
+{
+ log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName, libraryNameHint, address, module);
+
+ const mach_header* foundInImageAtLoadAddress;
+ if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+ *module = (NSModule)foundInImageAtLoadAddress;
+ return;
+ }
+
+ *address = 0;
+ *module = 0;
+}
+
+
+void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module)
+{
+ log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName, address, module);
+
+ const mach_header* foundInImageAtLoadAddress;
+ if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+ *module = (NSModule)foundInImageAtLoadAddress;
+ return;
+ }
+
+ *address = 0;
+ *module = 0;
+}
+
+const struct mach_header* _dyld_get_image_header_containing_address(const void* address)
+{
+ log_apis("_dyld_get_image_header_containing_address(%p)\n", address);
+
+ return dyld_image_header_containing_address(address);
+}
+
+#endif
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <mach/mach_time.h> // mach_absolute_time()
+#include <pthread/pthread.h>
+#include <libkern/OSAtomic.h>
+
+#include <vector>
+#include <algorithm>
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "libdyldEntryVector.h"
+#include "Logging.h"
+#include "Loading.h"
+#include "Tracing.h"
+#include "LaunchCache.h"
+#include "DyldSharedCache.h"
+#include "PathOverrides.h"
+#include "DyldCacheParser.h"
+
+extern const char** appleParams;
+
+// should be a header for these
+struct __cxa_range_t {
+ const void* addr;
+ size_t length;
+};
+extern "C" void __cxa_finalize_ranges(const __cxa_range_t ranges[], unsigned int count);
+
+VIS_HIDDEN bool gUseDyld3 = false;
+
+
+namespace dyld3 {
+
+class VIS_HIDDEN LoadedImage {
+public:
+ enum class State { uninited=3, beingInited=2, inited=0 };
+ typedef launch_cache::binary_format::Image BinaryImage;
+
+ LoadedImage(const mach_header* mh, const BinaryImage* bi);
+ bool operator==(const LoadedImage& rhs) const;
+ void init(const mach_header* mh, const BinaryImage* bi);
+ const mach_header* loadedAddress() const { return (mach_header*)((uintptr_t)_loadAddress & ~0x7ULL); }
+ State state() const { return (State)((uintptr_t)_loadAddress & 0x3ULL); }
+ const BinaryImage* image() const { return _image; }
+ bool neverUnload() const { return ((uintptr_t)_loadAddress & 0x4ULL); }
+ void setState(State s) { _loadAddress = (mach_header*)((((uintptr_t)_loadAddress) & ~0x3ULL) | (uintptr_t)s); }
+ void setNeverUnload() { _loadAddress = (mach_header*)(((uintptr_t)_loadAddress) | 0x4ULL); }
+
+private:
+ const mach_header* _loadAddress; // low bits: bit2=neverUnload, bit1/bit0 contain State
+ const BinaryImage* _image;
+};
+
+
+bool LoadedImage::operator==(const LoadedImage& rhs) const
+{
+ return (_image == rhs._image) && (loadedAddress() == rhs.loadedAddress());
+}
+
+
+
+struct VIS_HIDDEN DlopenCount {
+ bool operator==(const DlopenCount& rhs) const;
+ const mach_header* loadAddress;
+ uintptr_t refCount;
+};
+
+bool DlopenCount::operator==(const DlopenCount& rhs) const
+{
+ return (loadAddress == rhs.loadAddress) && (refCount == rhs.refCount);
+}
+
+LoadedImage::LoadedImage(const mach_header* mh, const BinaryImage* bi)
+ : _loadAddress(mh), _image(bi)
+{
+ assert(loadedAddress() == mh);
+ setState(State::uninited);
+}
+
+void LoadedImage::init(const mach_header* mh, const BinaryImage* bi)
+{
+ _loadAddress = mh;
+ _image = bi;
+ assert(loadedAddress() == mh);
+ setState(State::uninited);
+}
+
+// forward reference
+template <typename T, int C> class ReaderWriterChunkedVector;
+
+template <typename T, int C>
+class VIS_HIDDEN ChunkedVector {
+public:
+ static ChunkedVector<T,C>* make(uint32_t count);
+
+ void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+ void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop));
+ T* add(const T& value);
+ T* add(uint32_t count, const T values[]);
+ void remove(uint32_t index);
+ uint32_t count() const { return _inUseCount; }
+ uint32_t freeCount() const { return _allocCount - _inUseCount; }
+private:
+ T& element(uint32_t index) { return ((T*)_elements)[index]; }
+ const T& element(uint32_t index) const { return ((T*)_elements)[index]; }
+
+ friend class ReaderWriterChunkedVector<T,C>;
+
+ ChunkedVector<T,C>* _next = nullptr;
+ uint32_t _allocCount = C;
+ uint32_t _inUseCount = 0;
+ uint8_t _elements[C*sizeof(T)] = { 0 };
+};
+
+template <typename T, int C>
+class VIS_HIDDEN ReaderWriterChunkedVector {
+public:
+ T* add(uint32_t count, const T values[]);
+ T* add(const T& value) { return add(1, &value); }
+ T* addNoLock(uint32_t count, const T values[]);
+ T* addNoLock(const T& value) { return addNoLock(1, &value); }
+ void remove(const T& value);
+ uint32_t count() const;
+ void forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+ void forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop));
+ void forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+ T& operator[](size_t index);
+ uint32_t countNoLock() const;
+
+ void withReadLock(void (^withLock)()) const;
+ void withWriteLock(void (^withLock)()) const;
+ void acquireWriteLock();
+ void releaseWriteLock();
+ void dump(void (^callback)(const T& value)) const;
+
+private:
+ mutable pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
+ ChunkedVector<T,C> _firstChunk;
+};
+
+
+typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+
+static ReaderWriterChunkedVector<NotifyFunc, 4> sLoadNotifiers;
+static ReaderWriterChunkedVector<NotifyFunc, 4> sUnloadNotifiers;
+static ReaderWriterChunkedVector<LoadedImage, 4> sLoadedImages;
+static ReaderWriterChunkedVector<DlopenCount, 4> sDlopenRefCounts;
+static ReaderWriterChunkedVector<const launch_cache::BinaryImageGroupData*, 4> sKnownGroups;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static ReaderWriterChunkedVector<__NSObjectFileImage, 2> sNSObjectFileImages;
+#endif
+
+
+///////////////////// ChunkedVector ////////////////////////////
+
+template <typename T, int C>
+ChunkedVector<T,C>* ChunkedVector<T,C>::make(uint32_t count)
+{
+ size_t size = sizeof(ChunkedVector) + sizeof(T) * (count-C);
+ ChunkedVector<T,C>* result = (ChunkedVector<T,C>*)malloc(size);
+ result->_next = nullptr;
+ result->_allocCount = count;
+ result->_inUseCount = 0;
+ return result;
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+ for (uint32_t i=0; i < _inUseCount; ++i) {
+ callback(outerIndex, element(i), outerStop);
+ ++outerIndex;
+ if ( outerStop )
+ break;
+ }
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop))
+{
+ for (uint32_t i=0; i < _inUseCount; ++i) {
+ callback(outerIndex, element(i), outerStop);
+ ++outerIndex;
+ if ( outerStop )
+ break;
+ }
+}
+
+template <typename T, int C>
+T* ChunkedVector<T,C>::add(const T& value)
+{
+ return add(1, &value);
+}
+
+template <typename T, int C>
+T* ChunkedVector<T,C>::add(uint32_t count, const T values[])
+{
+ assert(count <= (_allocCount - _inUseCount));
+ T* result = &element(_inUseCount);
+ memmove(result, values, sizeof(T)*count);
+ _inUseCount += count;
+ return result;
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::remove(uint32_t index)
+{
+ assert(index < _inUseCount);
+ int moveCount = _inUseCount - index - 1;
+ if ( moveCount >= 1 ) {
+ memmove(&element(index), &element(index+1), sizeof(T)*moveCount);
+ }
+ _inUseCount--;
+}
+
+
+///////////////////// ReaderWriterChunkedVector ////////////////////////////
+
+
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::withReadLock(void (^work)()) const
+{
+ assert(pthread_rwlock_rdlock(&_lock) == 0);
+ work();
+ assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::withWriteLock(void (^work)()) const
+{
+ assert(pthread_rwlock_wrlock(&_lock) == 0);
+ work();
+ assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::acquireWriteLock()
+{
+ assert(pthread_rwlock_wrlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::releaseWriteLock()
+{
+ assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+uint32_t ReaderWriterChunkedVector<T,C>::count() const
+{
+ __block uint32_t result = 0;
+ withReadLock(^() {
+ for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ result += chunk->count();
+ }
+ });
+ return result;
+}
+
+template <typename T, int C>
+uint32_t ReaderWriterChunkedVector<T,C>::countNoLock() const
+{
+ uint32_t result = 0;
+ for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ result += chunk->count();
+ }
+ return result;
+}
+
+template <typename T, int C>
+T* ReaderWriterChunkedVector<T,C>::addNoLock(uint32_t count, const T values[])
+{
+ T* result = nullptr;
+ ChunkedVector<T,C>* lastChunk = &_firstChunk;
+ while ( lastChunk->_next != nullptr )
+ lastChunk = lastChunk->_next;
+
+ if ( lastChunk->freeCount() >= count ) {
+ // append to last chunk
+ result = lastChunk->add(count, values);
+ }
+ else {
+ // append new chunk
+ uint32_t allocCount = count;
+ uint32_t remainder = count % C;
+ if ( remainder != 0 )
+ allocCount = count + C - remainder;
+ ChunkedVector<T,C>* newChunk = ChunkedVector<T,C>::make(allocCount);
+ result = newChunk->add(count, values);
+ lastChunk->_next = newChunk;
+ }
+
+ return result;
+}
+
+template <typename T, int C>
+T* ReaderWriterChunkedVector<T,C>::add(uint32_t count, const T values[])
+{
+ __block T* result = nullptr;
+ withWriteLock(^() {
+ result = addNoLock(count, values);
+ });
+ return result;
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::remove(const T& valueToRemove)
+{
+ __block bool stopStorage = false;
+ withWriteLock(^() {
+ ChunkedVector<T,C>* chunkNowEmpty = nullptr;
+ __block uint32_t indexStorage = 0;
+ __block bool found = false;
+ for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ uint32_t chunkStartIndex = indexStorage;
+ __block uint32_t foundIndex = 0;
+ chunk->forEach(indexStorage, stopStorage, ^(uint32_t index, const T& value, bool& stop) {
+ if ( value == valueToRemove ) {
+ foundIndex = index - chunkStartIndex;
+ found = true;
+ stop = true;
+ }
+ });
+ if ( found ) {
+ chunk->remove(foundIndex);
+ found = false;
+ if ( chunk->count() == 0 )
+ chunkNowEmpty = chunk;
+ }
+ }
+ // if chunk is now empty, remove from linked list and free
+ if ( chunkNowEmpty ) {
+ for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ if ( chunk->_next == chunkNowEmpty ) {
+ chunk->_next = chunkNowEmpty->_next;
+ if ( chunkNowEmpty != &_firstChunk )
+ free(chunkNowEmpty);
+ break;
+ }
+ }
+ }
+ });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+ __block uint32_t index = 0;
+ __block bool stop = false;
+ withReadLock(^() {
+ for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ chunk->forEach(index, stop, callback);
+ if ( stop )
+ break;
+ }
+ });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop))
+{
+ __block uint32_t index = 0;
+ __block bool stop = false;
+ withReadLock(^() {
+ for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ chunk->forEach(index, stop, callback);
+ if ( stop )
+ break;
+ }
+ });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+ uint32_t index = 0;
+ bool stop = false;
+ for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ chunk->forEach(index, stop, callback);
+ if ( stop )
+ break;
+ }
+}
+
+template <typename T, int C>
+T& ReaderWriterChunkedVector<T,C>::operator[](size_t targetIndex)
+{
+ __block T* result = nullptr;
+ forEachNoLock(^(uint32_t index, T const& value, bool& stop) {
+ if ( index == targetIndex ) {
+ result = (T*)&value;
+ stop = true;
+ }
+ });
+ return *result;
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::dump(void (^callback)(const T& value)) const
+{
+ log("dump ReaderWriterChunkedVector at %p\n", this);
+ __block uint32_t index = 0;
+ __block bool stop = false;
+ withReadLock(^() {
+ for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+ log(" chunk at %p\n", chunk);
+ chunk->forEach(index, stop, ^(uint32_t i, const T& value, bool& s) {
+ callback(value);
+ });
+ }
+ });
+}
+
+
+
+///////////////////// AllImages ////////////////////////////
+
+
+AllImages gAllImages;
+
+
+
+void AllImages::init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath,
+ const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+{
+ _mainClosure = closure;
+ _initialImages = &initialImages;
+ _dyldCacheAddress = dyldCacheLoadAddress;
+ _dyldCachePath = dyldCachePath;
+
+ // Make temporary old image array, so libSystem initializers can be debugged
+ uint32_t count = (uint32_t)initialImages.count();
+ dyld_image_info oldDyldInfo[count];
+ for (int i=0; i < count; ++i) {
+ launch_cache::Image img(initialImages[i].imageData);
+ oldDyldInfo[i].imageLoadAddress = initialImages[i].loadAddress;
+ oldDyldInfo[i].imageFilePath = img.path();
+ oldDyldInfo[i].imageFileModDate = 0;
+ }
+ _oldAllImageInfos->infoArray = oldDyldInfo;
+ _oldAllImageInfos->infoArrayCount = count;
+ _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
+ _oldAllImageInfos->infoArray = nullptr;
+ _oldAllImageInfos->infoArrayCount = 0;
+}
+
+void AllImages::setProgramVars(ProgramVars* vars)
+{
+ _programVars = vars;
+}
+
+void AllImages::applyInitialImages()
+{
+ addImages(*_initialImages);
+ _initialImages = nullptr; // this was stack allocated
+}
+
+void AllImages::mirrorToOldAllImageInfos()
+{
+ // set infoArray to NULL to denote it is in-use
+ _oldAllImageInfos->infoArray = nullptr;
+
+ // if array not large enough, re-alloc it
+ uint32_t imageCount = sLoadedImages.countNoLock();
+ if ( _oldArrayAllocCount < imageCount ) {
+ uint32_t newAllocCount = imageCount + 16;
+ dyld_image_info* newArray = (dyld_image_info*)malloc(sizeof(dyld_image_info)*newAllocCount);
+ if ( _oldAllImageArray != nullptr ) {
+ memcpy(newArray, _oldAllImageArray, sizeof(dyld_image_info)*_oldAllImageInfos->infoArrayCount);
+ free(_oldAllImageArray);
+ }
+ _oldAllImageArray = newArray;
+ _oldArrayAllocCount = newAllocCount;
+ }
+
+ // fill out array to mirror current image list
+ sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& loadedImage, bool& stop) {
+ launch_cache::Image img(loadedImage.image());
+ _oldAllImageArray[index].imageLoadAddress = loadedImage.loadedAddress();
+ _oldAllImageArray[index].imageFilePath = imagePath(loadedImage.image());
+ _oldAllImageArray[index].imageFileModDate = 0;
+ });
+
+ // set infoArray back to base address of array (so other process can now read)
+ _oldAllImageInfos->infoArrayCount = imageCount;
+ _oldAllImageInfos->infoArrayChangeTimestamp = mach_absolute_time();
+ _oldAllImageInfos->infoArray = _oldAllImageArray;
+}
+
+void AllImages::addImages(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+{
+ uint32_t count = (uint32_t)newImages.count();
+ assert(count != 0);
+
+ // build stack array of LoadedImage to copy into sLoadedImages
+ STACK_ALLOC_DYNARRAY(LoadedImage, count, loadedImagesArray);
+ for (uint32_t i=0; i < count; ++i) {
+ loadedImagesArray[i].init(newImages[i].loadAddress, newImages[i].imageData);
+ if (newImages[i].neverUnload)
+ loadedImagesArray[i].setNeverUnload();
+ }
+ sLoadedImages.add(count, &loadedImagesArray[0]);
+
+ if ( _oldAllImageInfos != nullptr ) {
+ // sync to old all image infos struct
+ if ( _initialImages != nullptr ) {
+ // libSystem not initialized yet, don't use locks
+ mirrorToOldAllImageInfos();
+ }
+ else {
+ sLoadedImages.withReadLock(^{
+ mirrorToOldAllImageInfos();
+ });
+ }
+
+ // tell debugger about new images
+ dyld_image_info oldDyldInfo[count];
+ for (int i=0; i < count; ++i) {
+ launch_cache::Image img(newImages[i].imageData);
+ oldDyldInfo[i].imageLoadAddress = newImages[i].loadAddress;
+ oldDyldInfo[i].imageFilePath = imagePath(newImages[i].imageData);
+ oldDyldInfo[i].imageFileModDate = 0;
+ }
+ _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
+ }
+
+ // log loads
+ for (int i=0; i < count; ++i) {
+ launch_cache::Image img(newImages[i].imageData);
+ log_loads("dyld: %s\n", imagePath(newImages[i].imageData));
+ }
+
+#if !TARGET_IPHONE_SIMULATOR
+ // call kdebug trace for each image
+ if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
+ for (uint32_t i=0; i < count; ++i) {
+ launch_cache::Image img(newImages[i].imageData);
+ struct stat stat_buf;
+ fsid_t fsid = {{ 0, 0 }};
+ fsobj_id_t fsobjid = { 0, 0 };
+ if (img.isDiskImage() && stat(imagePath(newImages[i].imageData), &stat_buf) == 0 ) {
+ fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
+ fsid = {{ stat_buf.st_dev, 0 }};
+ }
+ kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress);
+ }
+ }
+#endif
+ // call each _dyld_register_func_for_add_image function with each image
+ const uint32_t existingNotifierCount = sLoadNotifiers.count();
+ NotifyFunc existingNotifiers[existingNotifierCount];
+ NotifyFunc* existingNotifierArray = existingNotifiers;
+ sLoadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
+ if ( index < existingNotifierCount )
+ existingNotifierArray[index] = func;
+ });
+ // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+ for (uint32_t j=0; j < existingNotifierCount; ++j) {
+ NotifyFunc func = existingNotifierArray[j];
+ for (uint32_t i=0; i < count; ++i) {
+ MachOParser parser(newImages[i].loadAddress);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress);
+ func(newImages[i].loadAddress, parser.getSlide());
+ }
+ }
+
+ // call objc about images that use objc
+ if ( _objcNotifyMapped != nullptr ) {
+ const char* pathsBuffer[count];
+ const mach_header* mhBuffer[count];
+ uint32_t imagesWithObjC = 0;
+ for (uint32_t i=0; i < count; ++i) {
+ launch_cache::Image img(newImages[i].imageData);
+ if ( img.hasObjC() ) {
+ pathsBuffer[imagesWithObjC] = imagePath(newImages[i].imageData);
+ mhBuffer[imagesWithObjC] = newImages[i].loadAddress;
+ ++imagesWithObjC;
+ }
+ }
+ if ( imagesWithObjC != 0 ) {
+ (*_objcNotifyMapped)(imagesWithObjC, pathsBuffer, mhBuffer);
+ if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
+ for (uint32_t i=0; i < imagesWithObjC; ++i) {
+ log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]);
+ }
+ }
+ }
+ }
+
+ // notify any processes tracking loads in this process
+ notifyMonitorLoads(newImages);
+}
+
+void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages)
+{
+ uint32_t count = (uint32_t)unloadImages.count();
+ assert(count != 0);
+
+ // call each _dyld_register_func_for_remove_image function with each image
+ // do this before removing image from internal data structures so that the callback can query dyld about the image
+ const uint32_t existingNotifierCount = sUnloadNotifiers.count();
+ NotifyFunc existingNotifiers[existingNotifierCount];
+ NotifyFunc* existingNotifierArray = existingNotifiers;
+ sUnloadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
+ if ( index < existingNotifierCount )
+ existingNotifierArray[index] = func;
+ });
+ // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+ for (uint32_t j=0; j < existingNotifierCount; ++j) {
+ NotifyFunc func = existingNotifierArray[j];
+ for (uint32_t i=0; i < count; ++i) {
+ MachOParser parser(unloadImages[i].loadAddress);
+ log_notifications("dyld: remove notifier %p called with mh=%p\n", func, unloadImages[i].loadAddress);
+ func(unloadImages[i].loadAddress, parser.getSlide());
+ }
+ }
+
+ // call objc about images going away
+ if ( _objcNotifyUnmapped != nullptr ) {
+ for (uint32_t i=0; i < count; ++i) {
+ launch_cache::Image img(unloadImages[i].imageData);
+ if ( img.hasObjC() ) {
+ (*_objcNotifyUnmapped)(imagePath(unloadImages[i].imageData), unloadImages[i].loadAddress);
+ log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", unloadImages[i].loadAddress, imagePath(unloadImages[i].imageData));
+ }
+ }
+ }
+
+#if !TARGET_IPHONE_SIMULATOR
+ // call kdebug trace for each image
+ if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
+ for (uint32_t i=0; i < count; ++i) {
+ launch_cache::Image img(unloadImages[i].imageData);
+ struct stat stat_buf;
+ fsid_t fsid = {{ 0, 0 }};
+ fsobj_id_t fsobjid = { 0, 0 };
+ if (stat(imagePath(unloadImages[i].imageData), &stat_buf) == 0 ) {
+ fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
+ fsid = {{ stat_buf.st_dev, 0 }};
+ }
+ kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress);
+ }
+ }
+#endif
+
+ // remove each from sLoadedImages
+ for (uint32_t i=0; i < count; ++i) {
+ LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData);
+ sLoadedImages.remove(info);
+ }
+
+ // sync to old all image infos struct
+ sLoadedImages.withReadLock(^{
+ mirrorToOldAllImageInfos();
+ });
+
+ // tell debugger about removed images
+ dyld_image_info oldDyldInfo[count];
+ for (int i=0; i < count; ++i) {
+ launch_cache::Image img(unloadImages[i].imageData);
+ oldDyldInfo[i].imageLoadAddress = unloadImages[i].loadAddress;
+ oldDyldInfo[i].imageFilePath = imagePath(unloadImages[i].imageData);
+ oldDyldInfo[i].imageFileModDate = 0;
+ }
+ _oldAllImageInfos->notification(dyld_image_removing, count, oldDyldInfo);
+
+ // unmap images
+ for (int i=0; i < count; ++i) {
+ launch_cache::Image img(unloadImages[i].imageData);
+ loader::unmapImage(unloadImages[i].imageData, unloadImages[i].loadAddress);
+ log_loads("dyld: unloaded %s\n", imagePath(unloadImages[i].imageData));
+ }
+
+ // notify any processes tracking loads in this process
+ notifyMonitorUnloads(unloadImages);
+}
+
+void AllImages::setNeverUnload(const loader::ImageInfo& existingImage)
+{
+ sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) {
+ if (value.image() == existingImage.imageData) {
+ value.setNeverUnload();
+ stop = true;
+ }
+ });
+}
+
+uint32_t AllImages::count() const
+{
+ return sLoadedImages.count();
+}
+
+
+launch_cache::Image AllImages::findByLoadOrder(uint32_t index, const mach_header** loadAddress) const
+{
+ __block const BinaryImage* foundImage = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( anIndex == index ) {
+ foundImage = loadedImage.image();
+ *loadAddress = loadedImage.loadedAddress();
+ stop = true;
+ }
+ });
+ return launch_cache::Image(foundImage);
+}
+
+launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const
+{
+ __block const BinaryImage* foundImage = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( loadedImage.loadedAddress() == loadAddress ) {
+ foundImage = loadedImage.image();
+ stop = true;
+ }
+ });
+ return launch_cache::Image(foundImage);
+}
+
+bool AllImages::findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index)
+{
+ __block bool result = false;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( loadedImage.loadedAddress() == loadAddress ) {
+ index = anIndex;
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const
+{
+ sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) {
+ handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop);
+ });
+}
+
+launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const
+{
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so sLoadedImages not allocated yet
+ for (int i=0; i < _initialImages->count(); ++i) {
+ const loader::ImageInfo& entry = (*_initialImages)[i];
+ launch_cache::Image anImage(entry.imageData);
+ if ( anImage.containsAddress(addr, entry.loadAddress, permissions) ) {
+ *loadAddress = entry.loadAddress;
+ return entry.imageData;
+ }
+ }
+ return launch_cache::Image(nullptr);
+ }
+
+ // if address is in cache, do fast search of cache
+ if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+ const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
+ if ( addr < (void*)((uint8_t*)_dyldCacheAddress+dyldCache->mappedSize()) ) {
+ size_t cacheVmOffset = ((uint8_t*)addr - (uint8_t*)_dyldCacheAddress);
+ DyldCacheParser cacheParser(dyldCache, false);
+ launch_cache::ImageGroup cachedDylibsGroup(cacheParser.cachedDylibsGroup());
+ uint32_t mhCacheOffset;
+ uint8_t foundPermissions;
+ launch_cache::Image image(cachedDylibsGroup.findImageByCacheOffset(cacheVmOffset, mhCacheOffset, foundPermissions));
+ if ( image.valid() ) {
+ *loadAddress = (mach_header*)((uint8_t*)_dyldCacheAddress + mhCacheOffset);
+ if ( permissions != nullptr )
+ *permissions = foundPermissions;
+ return image;
+ }
+ }
+ }
+
+ __block const BinaryImage* foundImage = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ launch_cache::Image anImage(loadedImage.image());
+ if ( anImage.containsAddress(addr, loadedImage.loadedAddress(), permissions) ) {
+ *loadAddress = loadedImage.loadedAddress();
+ foundImage = loadedImage.image();
+ stop = true;
+ }
+ });
+ return launch_cache::Image(foundImage);
+}
+
+const mach_header* AllImages::findLoadAddressByImage(const BinaryImage* targetImage) const
+{
+ __block const mach_header* foundAddress = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( targetImage == loadedImage.image() ) {
+ foundAddress = loadedImage.loadedAddress();
+ stop = true;
+ }
+ });
+ return foundAddress;
+}
+
+const mach_header* AllImages::mainExecutable() const
+{
+ assert(_programVars != nullptr);
+ return (const mach_header*)_programVars->mh;
+}
+
+launch_cache::Image AllImages::mainExecutableImage() const
+{
+ assert(_mainClosure != nullptr);
+ const launch_cache::Closure mainClosure(_mainClosure);
+ const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group();
+ const uint32_t mainExecutableIndex = mainClosure.mainExecutableImageIndex();
+ const dyld3::launch_cache::Image mainImage = mainGroup.image(mainExecutableIndex);
+ return mainImage;
+}
+
+void AllImages::setMainPath(const char* path )
+{
+ _mainExeOverridePath = path;
+}
+
+const char* AllImages::imagePath(const BinaryImage* binImage) const
+{
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // on iOS and watchOS, apps may be moved on device after closure built
+ if ( _mainExeOverridePath != nullptr ) {
+ if ( binImage == mainExecutableImage().binaryData() )
+ return _mainExeOverridePath;
+ }
+#endif
+ launch_cache::Image image(binImage);
+ return image.path();
+}
+
+void AllImages::setInitialGroups()
+{
+ DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
+ sKnownGroups.addNoLock(cacheParser.cachedDylibsGroup());
+ sKnownGroups.addNoLock(cacheParser.otherDylibsGroup());
+ launch_cache::Closure closure(_mainClosure);
+ sKnownGroups.addNoLock(closure.group().binaryData());
+}
+
+const launch_cache::binary_format::ImageGroup* AllImages::cachedDylibsGroup()
+{
+ return sKnownGroups[0];
+}
+
+const launch_cache::binary_format::ImageGroup* AllImages::otherDylibsGroup()
+{
+ return sKnownGroups[1];
+}
+
+const AllImages::BinaryImageGroup* AllImages::mainClosureGroup()
+{
+ return sKnownGroups[2];
+}
+
+uint32_t AllImages::currentGroupsCount() const
+{
+ return sKnownGroups.count();
+}
+
+void AllImages::copyCurrentGroups(ImageGroupList& groups) const
+{
+ sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
+ if ( index < groups.count() )
+ groups[index] = grpData;
+ });
+}
+
+void AllImages::copyCurrentGroupsNoLock(ImageGroupList& groups) const
+{
+ sKnownGroups.forEachNoLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
+ if ( index < groups.count() )
+ groups[index] = grpData;
+ });
+}
+
+const mach_header* AllImages::alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount)
+{
+ __block const mach_header* result = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ launch_cache::Image img(loadedImage.image());
+ if ( img.validateUsingModTimeAndInode() ) {
+ if ( (img.fileINode() == inode) && (img.fileModTime() == mtime) ) {
+ result = loadedImage.loadedAddress();
+ if ( bumpRefCount && !loadedImage.neverUnload() )
+ incRefCount(loadedImage.loadedAddress());
+ stop = true;
+ }
+ }
+ });
+ return result;
+}
+
+const mach_header* AllImages::alreadyLoaded(const char* path, bool bumpRefCount)
+{
+ __block const mach_header* result = nullptr;
+ uint32_t targetHash = launch_cache::ImageGroup::hashFunction(path);
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ launch_cache::Image img(loadedImage.image());
+ if ( (img.pathHash() == targetHash) && (strcmp(path, imagePath(loadedImage.image())) == 0) ) {
+ result = loadedImage.loadedAddress();
+ if ( bumpRefCount && !loadedImage.neverUnload() )
+ incRefCount(loadedImage.loadedAddress());
+ stop = true;
+ }
+ });
+ if ( result == nullptr ) {
+ // perhaps there was an image override
+ launch_cache::ImageGroup mainGroup(mainClosureGroup());
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
+ copyCurrentGroups(currentGroupsList);
+ mainGroup.forEachImageRefOverride(currentGroupsList, ^(launch_cache::Image standardDylib, launch_cache::Image overrideDyilb, bool& stop) {
+ if ( strcmp(standardDylib.path(), path) == 0 ) {
+ result = alreadyLoaded(overrideDyilb.path(), bumpRefCount);
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+const mach_header* AllImages::alreadyLoaded(const BinaryImage* binImage, bool bumpRefCount)
+{
+ const mach_header* result = findLoadAddressByImage(binImage);
+ if ( result != nullptr ) {
+ launch_cache::Image loadedImage(binImage);
+ if ( bumpRefCount && !loadedImage.neverUnload() )
+ incRefCount(result);
+ }
+ return result;
+}
+
+void AllImages::incRefCount(const mach_header* loadAddress)
+{
+ __block bool found = false;
+ sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+ if ( entry.loadAddress == loadAddress ) {
+ found = true;
+ entry.refCount += 1;
+ stop = true;
+ }
+ });
+ if ( !found ) {
+ DlopenCount newEnty = { loadAddress, 1 };
+ sDlopenRefCounts.add(newEnty);
+ }
+}
+
+void AllImages::decRefCount(const mach_header* loadAddress)
+{
+ __block bool refCountNowZero = false;
+ sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+ if ( entry.loadAddress == loadAddress ) {
+ entry.refCount -= 1;
+ stop = true;
+ if ( entry.refCount == 0 )
+ refCountNowZero = true;
+ }
+ });
+ if ( refCountNowZero ) {
+ DlopenCount delEnty = { loadAddress, 0 };
+ sDlopenRefCounts.remove(delEnty);
+ garbageCollectImages();
+ }
+}
+
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+__NSObjectFileImage* AllImages::addNSObjectFileImage()
+{
+ // look for empty slot first
+ __block __NSObjectFileImage* result = nullptr;
+ sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
+ if ( (value.path == nullptr) && (value.memSource == nullptr) ) {
+ result = &value;
+ stop = true;
+ }
+ });
+ if ( result != nullptr )
+ return result;
+
+ // otherwise allocate new slot
+ __NSObjectFileImage empty;
+ return sNSObjectFileImages.add(empty);
+}
+
+bool AllImages::hasNSObjectFileImage(__NSObjectFileImage* ofi)
+{
+ __block bool result = false;
+ sNSObjectFileImages.forEachNoLock(^(uint32_t index, const __NSObjectFileImage& value, bool& stop) {
+ if ( &value == ofi ) {
+ result = ((value.memSource != nullptr) || (value.path != nullptr));
+ stop = true;
+ }
+ });
+ return result;
+}
+
+void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
+{
+ sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
+ if ( &value == ofi ) {
+ // mark slot as empty
+ ofi->path = nullptr;
+ ofi->memSource = nullptr;
+ ofi->memLength = 0;
+ ofi->loadAddress = nullptr;
+ ofi->binImage = nullptr;
+ stop = true;
+ }
+ });
+}
+#endif
+
+
+class VIS_HIDDEN Reaper
+{
+public:
+ Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray);
+ void garbageCollect();
+ void finalizeDeadImages();
+
+private:
+ typedef launch_cache::binary_format::Image BinaryImage;
+
+ void markDirectlyDlopenedImagesAsUsed();
+ void markDependentOfInUseImages();
+ void markDependentsOf(const LoadedImage*);
+ bool loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& index);
+ bool imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex);
+ uint32_t inUseCount();
+ void dump(const char* msg);
+
+ const LoadedImage** _unloadablesArray;
+ bool* _inUseArray;
+ uint32_t _arrayCount;
+ uint32_t _deadCount;
+};
+
+Reaper::Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray)
+ : _unloadablesArray(unloadables), _inUseArray(inUseArray),_arrayCount(count)
+{
+}
+
+
+bool Reaper::loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& foundIndex)
+{
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ if ( _unloadablesArray[i]->loadedAddress() == loadAddr ) {
+ foundIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Reaper::imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex)
+{
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ if ( _unloadablesArray[i]->image() == binImage ) {
+ foundIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+void Reaper::markDirectlyDlopenedImagesAsUsed()
+{
+ sDlopenRefCounts.forEachWithReadLock(^(uint32_t refCountIndex, const dyld3::DlopenCount& dlEntry, bool& stop) {
+ if ( dlEntry.refCount != 0 ) {
+ uint32_t foundIndex;
+ if ( loadAddressIsUnloadable(dlEntry.loadAddress, foundIndex) ) {
+ _inUseArray[foundIndex] = true;
+ }
+ }
+ });
+}
+
+uint32_t Reaper::inUseCount()
+{
+ uint32_t count = 0;
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ if ( _inUseArray[i] )
+ ++count;
+ }
+ return count;
+}
+
+void Reaper::markDependentsOf(const LoadedImage* entry)
+{
+ const launch_cache::Image image(entry->image());
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+ gAllImages.copyCurrentGroups(currentGroupsList);
+ image.forEachDependentImage(currentGroupsList, ^(uint32_t depIndex, dyld3::launch_cache::Image depImage, dyld3::launch_cache::Image::LinkKind kind, bool& stop) {
+ uint32_t foundIndex;
+ if ( !depImage.neverUnload() && imageIsUnloadable(depImage.binaryData(), foundIndex) ) {
+ _inUseArray[foundIndex] = true;
+ }
+ });
+}
+
+void Reaper::markDependentOfInUseImages()
+{
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ if ( _inUseArray[i] )
+ markDependentsOf(_unloadablesArray[i]);
+ }
+}
+
+void Reaper::dump(const char* msg)
+{
+ //log("%s:\n", msg);
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
+ //log(" in-used=%d %s\n", _inUseArray[i], image.path());
+ }
+}
+
+void Reaper::garbageCollect()
+{
+ //dump("all unloadable images");
+
+ // mark all dylibs directly dlopen'ed as in use
+ markDirectlyDlopenedImagesAsUsed();
+
+ //dump("directly dlopen()'ed marked");
+
+ // iteratively mark dependents of in-use dylibs as in-use until in-use count stops changing
+ uint32_t lastCount = inUseCount();
+ bool countChanged = false;
+ do {
+ markDependentOfInUseImages();
+ //dump("dependents marked");
+ uint32_t newCount = inUseCount();
+ countChanged = (newCount != lastCount);
+ lastCount = newCount;
+ } while (countChanged);
+
+ _deadCount = _arrayCount - inUseCount();
+}
+
+void Reaper::finalizeDeadImages()
+{
+ if ( _deadCount == 0 )
+ return;
+ __cxa_range_t ranges[_deadCount];
+ __cxa_range_t* rangesArray = ranges;
+ __block unsigned int rangesCount = 0;
+ for (uint32_t i=0; i < _arrayCount; ++i) {
+ if ( _inUseArray[i] )
+ continue;
+ dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+ if ( permissions & VM_PROT_EXECUTE ) {
+ rangesArray[rangesCount].addr = (char*)(_unloadablesArray[i]->loadedAddress()) + vmOffset;
+ rangesArray[rangesCount].length = (size_t)vmSize;
+ ++rangesCount;
+ }
+ });
+ }
+ __cxa_finalize_ranges(ranges, rangesCount);
+}
+
+
+// This function is called at the end of dlclose() when the reference count goes to zero.
+// The dylib being unloaded may have brought in other dependent dylibs when it was loaded.
+// Those dependent dylibs need to be unloaded, but only if they are not referenced by
+// something else. We use a standard mark and sweep garbage collection.
+//
+// The tricky part is that when a dylib is unloaded it may have a termination function that
+// can run and itself call dlclose() on yet another dylib. The problem is that this
+// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose()
+// which calls garbageCollectImages() will just set a flag to re-do the garbage collection
+// when the current pass is done.
+//
+// Also note that this is done within the sLoadedImages writer lock, so any dlopen/dlclose
+// on other threads are blocked while this garbage collections runs
+//
+void AllImages::garbageCollectImages()
+{
+ // if some other thread is currently GC'ing images, let other thread do the work
+ int32_t newCount = OSAtomicIncrement32(&_gcCount);
+ if ( newCount != 1 )
+ return;
+
+ do {
+ const uint32_t loadedImageCount = sLoadedImages.count();
+ const LoadedImage* unloadables[loadedImageCount];
+ bool unloadableInUse[loadedImageCount];
+ const LoadedImage** unloadablesArray = unloadables;
+ bool* unloadableInUseArray = unloadableInUse;
+ __block uint32_t unloadableCount = 0;
+ // do GC with lock, so no other images can be added during GC
+ sLoadedImages.withReadLock(^() {
+ sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
+ const launch_cache::Image image(entry.image());
+ if ( !image.neverUnload() && !entry.neverUnload() ) {
+ unloadablesArray[unloadableCount] = &entry;
+ unloadableInUseArray[unloadableCount] = false;
+ //log("unloadable[%d] %p %s\n", unloadableCount, entry.loadedAddress(), image.path());
+ ++unloadableCount;
+ }
+ });
+ // make reaper object to do garbage collection and notifications
+ Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray);
+ reaper.garbageCollect();
+
+ // FIXME: we should sort dead images so higher level ones are terminated first
+
+ // call cxa_finalize_ranges of dead images
+ reaper.finalizeDeadImages();
+
+ // FIXME: call static terminators of dead images
+
+ // FIXME: DOF unregister
+ });
+
+ //log("sLoadedImages before GC removals:\n");
+ //sLoadedImages.dump(^(const LoadedImage& entry) {
+ // const launch_cache::Image image(entry.image());
+ // log(" loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path());
+ //});
+
+ // make copy of LoadedImages we want to remove
+ // because unloadables[] points into ChunkVector we are shrinking
+ uint32_t removalCount = 0;
+ for (uint32_t i=0; i < unloadableCount; ++i) {
+ if ( !unloadableInUse[i] )
+ ++removalCount;
+ }
+ if ( removalCount > 0 ) {
+ STACK_ALLOC_DYNARRAY(loader::ImageInfo, removalCount, unloadImages);
+ uint32_t removalIndex = 0;
+ for (uint32_t i=0; i < unloadableCount; ++i) {
+ if ( !unloadableInUse[i] ) {
+ unloadImages[removalIndex].loadAddress = unloadables[i]->loadedAddress();
+ unloadImages[removalIndex].imageData = unloadables[i]->image();
+ ++removalIndex;
+ }
+ }
+ // remove entries from sLoadedImages
+ removeImages(unloadImages);
+
+ //log("sLoadedImages after GC removals:\n");
+ //sLoadedImages.dump(^(const LoadedImage& entry) {
+ // const launch_cache::Image image(entry.image());
+ // //log(" loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path());
+ //});
+ }
+
+ // if some other thread called GC during our work, redo GC on its behalf
+ newCount = OSAtomicDecrement32(&_gcCount);
+ }
+ while (newCount > 0);
+}
+
+
+
+VIS_HIDDEN
+const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount)
+{
+ __block const launch_cache::binary_format::Image* result = nullptr;
+ sKnownGroups.withWriteLock(^() {
+ ClosureBuffer::CacheIdent cacheIdent;
+ bzero(&cacheIdent, sizeof(cacheIdent));
+ if ( _dyldCacheAddress != nullptr ) {
+ const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
+ dyldCache->getUUID(cacheIdent.cacheUUID);
+ cacheIdent.cacheAddress = (unsigned long)_dyldCacheAddress;
+ cacheIdent.cacheMappedSize = dyldCache->mappedSize();
+ }
+ gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stopVariants) {
+ struct stat statBuf;
+ if ( stat(possiblePath, &statBuf) == 0 ) {
+ if ( S_ISDIR(statBuf.st_mode) ) {
+ log_apis(" %s: path is directory: %s\n", apiName, possiblePath);
+ if ( closuredErrorMessagesCount < 3 )
+ closuredErrorMessages[closuredErrorMessagesCount++] = strdup("not a file");
+ }
+ else {
+ // file exists, ask closured to build info for it
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, sKnownGroups.countNoLock(), currentGroupsList);
+ gAllImages.copyCurrentGroupsNoLock(currentGroupsList);
+ dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> nonCacheGroupList(currentGroupsList.count()-2, ¤tGroupsList[2]);
+ const dyld3::launch_cache::binary_format::ImageGroup* closuredCreatedGroupData = nullptr;
+ ClosureBuffer closureBuilderInput(cacheIdent, path, nonCacheGroupList, gPathOverrides);
+ ClosureBuffer closureBuilderOutput = dyld3::closured_CreateImageGroup(closureBuilderInput);
+ if ( !closureBuilderOutput.isError() ) {
+ vm_protect(mach_task_self(), closureBuilderOutput.vmBuffer(), closureBuilderOutput.vmBufferSize(), false, VM_PROT_READ);
+ closuredCreatedGroupData = closureBuilderOutput.imageGroup();
+ log_apis(" %s: closured built ImageGroup for path: %s\n", apiName, possiblePath);
+ sKnownGroups.addNoLock(closuredCreatedGroupData);
+ launch_cache::ImageGroup group(closuredCreatedGroupData);
+ result = group.imageBinary(0);
+ stopVariants = true;
+ }
+ else {
+ log_apis(" %s: closured failed for path: %s, error: %s\n", apiName, possiblePath, closureBuilderOutput.errorMessage());
+ if ( closuredErrorMessagesCount < 3 ) {
+ closuredErrorMessages[closuredErrorMessagesCount++] = strdup(closureBuilderOutput.errorMessage());
+ }
+ closureBuilderOutput.free();
+ }
+ }
+ }
+ else {
+ log_apis(" %s: file does not exist for path: %s\n", apiName, possiblePath);
+ }
+ });
+ });
+
+ return result;
+}
+
+const AllImages::BinaryImage* AllImages::findImageInKnownGroups(const char* path)
+{
+ __block const AllImages::BinaryImage* result = nullptr;
+ sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const& grpData, bool& stop) {
+ launch_cache::ImageGroup group(grpData);
+ uint32_t ignore;
+ if ( const AllImages::BinaryImage* binImage = group.findImageByPath(path, ignore) ) {
+ result = binImage;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const
+{
+ // check if statically determined in clousre that this can never be unloaded
+ if ( image.neverUnload() )
+ return false;
+
+ // check if some runtime decision made this be never-unloadable
+ __block bool foundAsNeverUnload = false;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( loadedImage.loadedAddress() == loadAddress ) {
+ stop = true;
+ if ( loadedImage.neverUnload() )
+ foundAsNeverUnload = true;
+ }
+ });
+ if ( foundAsNeverUnload )
+ return false;
+
+ return true;
+}
+
+void AllImages::addLoadNotifier(NotifyFunc func)
+{
+ // callback about already loaded images
+ const uint32_t existingCount = sLoadedImages.count();
+ const mach_header* existingMHs[existingCount];
+ const mach_header** existingArray = existingMHs;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ if ( anIndex < existingCount )
+ existingArray[anIndex] = loadedImage.loadedAddress();
+ });
+ // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+ for (uint32_t i=0; i < existingCount; i++) {
+ MachOParser parser(existingArray[i]);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, existingArray[i]);
+ func(existingArray[i], parser.getSlide());
+ }
+
+ // add to list of functions to call about future loads
+ sLoadNotifiers.add(func);
+}
+
+void AllImages::addUnloadNotifier(NotifyFunc func)
+{
+ // add to list of functions to call about future unloads
+ sUnloadNotifiers.add(func);
+}
+
+void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
+{
+ _objcNotifyMapped = map;
+ _objcNotifyInit = init;
+ _objcNotifyUnmapped = unmap;
+
+ // callback about already loaded images
+ uint32_t maxCount = count();
+ const char* pathsBuffer[maxCount];
+ const mach_header* mhBuffer[maxCount];
+ __block const char** paths = pathsBuffer;
+ __block const mach_header** mhs = mhBuffer;
+ __block uint32_t imagesWithObjC = 0;
+ sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+ launch_cache::Image img(loadedImage.image());
+ if ( img.hasObjC() ) {
+ mhs[imagesWithObjC] = loadedImage.loadedAddress();
+ paths[imagesWithObjC] = imagePath(loadedImage.image());
+ ++imagesWithObjC;
+ }
+ });
+ if ( imagesWithObjC != 0 ) {
+ (*map)(imagesWithObjC, pathsBuffer, mhBuffer);
+ if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
+ for (uint32_t i=0; i < imagesWithObjC; ++i) {
+ log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]);
+ }
+ }
+ }
+}
+
+void AllImages::vmAccountingSetSuspended(bool suspend)
+{
+#if __arm__ || __arm64__
+ // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
+ log_fixups("vm.footprint_suspend=%d\n", suspend);
+ int newValue = suspend ? 1 : 0;
+ int oldValue = 0;
+ size_t newlen = sizeof(newValue);
+ size_t oldlen = sizeof(oldValue);
+ sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+#endif
+}
+
+void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+{
+ launch_cache::Closure mainClosure(closure);
+ launch_cache::ImageGroup mainGroup = mainClosure.group();
+ DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
+ const launch_cache::binary_format::ImageGroup* dylibsGroupData = cacheParser.cachedDylibsGroup();
+ launch_cache::ImageGroup dyldCacheDylibGroup(dylibsGroupData);
+ __block bool suspendedAccounting = false;
+ mainGroup.forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, const launch_cache::binary_format::Image* imageData, uint32_t imageOffset, bool& stop) {
+ bool foundInImages = false;
+ for (int i=0; i < initialImages.count(); ++i) {
+ if ( initialImages[i].imageData == imageData ) {
+ foundInImages = true;
+ uintptr_t replacement = (uintptr_t)(initialImages[i].loadAddress) + imageOffset;
+ dyldCacheDylibGroup.forEachDyldCachePatchLocation(_dyldCacheAddress, patchTableIndex, ^(uintptr_t* locationToPatch, uintptr_t addend, bool& innerStop) {
+ if ( !suspendedAccounting ) {
+ vmAccountingSetSuspended(true);
+ suspendedAccounting = true;
+ }
+ log_fixups("dyld: cache fixup: *%p = %p\n", locationToPatch, (void*)replacement);
+ *locationToPatch = replacement + addend;
+ });
+ break;
+ }
+ }
+ if ( !foundInImages ) {
+ launch_cache::Image img(imageData);
+ log_fixups("did not find loaded image to patch into cache: %s\n", img.path());
+ }
+ });
+ if ( suspendedAccounting )
+ vmAccountingSetSuspended(false);
+}
+
+void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage)
+{
+ // run all initializers in image
+ launch_cache::Image libSystemImage(libSystemBinImage);
+ libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) {
+ Initializer initFunc = (Initializer)func;
+ dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
+ });
+ log_initializers("called initialzer %p in %s\n", initFunc, libSystemImage.path());
+ });
+
+ // mark libSystem.dylib as being init, so later recursive-init would re-run it
+ sLoadedImages.forEachWithWriteLock(^(uint32_t anIndex, LoadedImage& loadedImage, bool& stop) {
+ if ( loadedImage.loadedAddress() == libSystemAddress ) {
+ loadedImage.setState(LoadedImage::State::inited);
+ stop = true;
+ }
+ });
+}
+
+void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress)
+{
+ launch_cache::Image topImage = findByLoadAddress(imageLoadAddress);
+ if ( topImage.isInvalid() )
+ return;
+
+ // closure contains list of intializers to run in-order
+ STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
+ copyCurrentGroups(currentGroupsList);
+ topImage.forEachInitBefore(currentGroupsList, ^(launch_cache::Image imageToInit) {
+ // find entry
+ __block LoadedImage* foundEntry = nullptr;
+ sLoadedImages.forEachWithReadLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
+ if ( entry.image() == imageToInit.binaryData() ) {
+ foundEntry = (LoadedImage*)&entry;
+ stop = true;
+ }
+ });
+ assert(foundEntry != nullptr);
+ pthread_mutex_lock(&_initializerLock);
+ // Note, due to the large lock in dlopen, we can't be waiting on another thread
+ // here, but its possible that we are in a dlopen which is initialising us again
+ if ( foundEntry->state() == LoadedImage::State::beingInited ) {
+ log_initializers("dyld: already initializing '%s'\n", imagePath(imageToInit.binaryData()));
+ }
+ // at this point, the image is either initialized or not
+ // if not, initialize it on this thread
+ if ( foundEntry->state() == LoadedImage::State::uninited ) {
+ foundEntry->setState(LoadedImage::State::beingInited);
+ // release initializer lock, so other threads can run initializers
+ pthread_mutex_unlock(&_initializerLock);
+ // tell objc to run any +load methods in image
+ if ( (_objcNotifyInit != nullptr) && imageToInit.mayHavePlusLoads() ) {
+ log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", foundEntry->loadedAddress(), imagePath(imageToInit.binaryData()));
+ (*_objcNotifyInit)(imagePath(imageToInit.binaryData()), foundEntry->loadedAddress());
+ }
+ // run all initializers in image
+ imageToInit.forEachInitializer(foundEntry->loadedAddress(), ^(const void* func) {
+ Initializer initFunc = (Initializer)func;
+ dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
+ });
+ log_initializers("dyld: called initialzer %p in %s\n", initFunc, imageToInit.path());
+ });
+ // reaquire initializer lock to switch state to inited
+ pthread_mutex_lock(&_initializerLock);
+ foundEntry->setState(LoadedImage::State::inited);
+ }
+ pthread_mutex_unlock(&_initializerLock);
+ });
+}
+
+
+} // namespace dyld3
+
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 __ALL_IMAGES_H__
+#define __ALL_IMAGES_H__
+
+#include <pthread.h>
+#include <mach-o/loader.h>
+
+#include "dyld_priv.h"
+
+#include "LaunchCache.h"
+#include "Loading.h"
+
+
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+// only in macOS and deprecated
+struct VIS_HIDDEN __NSObjectFileImage
+{
+ const char* path = nullptr;
+ const void* memSource = nullptr;
+ size_t memLength = 0;
+ const mach_header* loadAddress = nullptr;
+ const dyld3::launch_cache::binary_format::Image* binImage = nullptr;
+};
+#endif
+
+namespace dyld3 {
+
+class VIS_HIDDEN AllImages
+{
+public:
+ typedef launch_cache::binary_format::Closure BinaryClosure;
+ typedef launch_cache::binary_format::ImageGroup BinaryImageGroup;
+ typedef launch_cache::binary_format::Image BinaryImage;
+ typedef launch_cache::ImageGroupList ImageGroupList;
+ typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+
+ void init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath,
+ const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages);
+ void setMainPath(const char* path);
+ void applyInitialImages();
+
+ void addImages(const dyld3::launch_cache::DynArray<loader::ImageInfo>& newImages);
+ void removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages);
+ void setNeverUnload(const loader::ImageInfo& existingImage);
+ void applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages);
+ void runInitialzersBottomUp(const mach_header* imageLoadAddress);
+ void setInitialGroups();
+
+ uint32_t count() const;
+ const BinaryImageGroup* cachedDylibsGroup();
+ const BinaryImageGroup* otherDylibsGroup();
+ const BinaryImageGroup* mainClosureGroup();
+ const BinaryClosure* mainClosure() { return _mainClosure; }
+ uint32_t currentGroupsCount() const;
+ void copyCurrentGroups(ImageGroupList& groups) const;
+
+ const BinaryImage* messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount);
+
+ launch_cache::Image findByLoadOrder(uint32_t index, const mach_header** loadAddress) const;
+ launch_cache::Image findByLoadAddress(const mach_header* loadAddress) const;
+ launch_cache::Image findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions=nullptr) const;
+ const mach_header* findLoadAddressByImage(const BinaryImage*) const;
+ bool findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index);
+ void forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const;
+
+ const mach_header* mainExecutable() const;
+ launch_cache::Image mainExecutableImage() const;
+ const void* cacheLoadAddress() const { return _dyldCacheAddress; }
+ const char* dyldCachePath() const { return _dyldCachePath; }
+ const char* imagePath(const BinaryImage*) const;
+
+ const mach_header* alreadyLoaded(const char* path, bool bumpRefCount);
+ const mach_header* alreadyLoaded(const BinaryImage*, bool bumpRefCount);
+ const mach_header* alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount);
+ const BinaryImage* findImageInKnownGroups(const char* path);
+
+ bool imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const;
+ void incRefCount(const mach_header* loadAddress);
+ void decRefCount(const mach_header* loadAddress);
+
+ void addLoadNotifier(NotifyFunc);
+ void addUnloadNotifier(NotifyFunc);
+ void setObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
+ void notifyObjCUnmap(const char* path, const struct mach_header* mh);
+
+ void runLibSystemInitializer(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+
+ void setOldAllImageInfo(dyld_all_image_infos* old) { _oldAllImageInfos = old; }
+ dyld_all_image_infos* oldAllImageInfo() const { return _oldAllImageInfos;}
+
+ void notifyMonitorMain();
+ void notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages);
+ void notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ __NSObjectFileImage* addNSObjectFileImage();
+ bool hasNSObjectFileImage(__NSObjectFileImage*);
+ void removeNSObjectFileImage(__NSObjectFileImage*);
+#endif
+
+ struct ProgramVars
+ {
+ const void* mh;
+ int* NXArgcPtr;
+ const char*** NXArgvPtr;
+ const char*** environPtr;
+ const char** __prognamePtr;
+ };
+ void setProgramVars(ProgramVars* vars);
+
+private:
+
+ typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
+ typedef const launch_cache::DynArray<loader::ImageInfo> StartImageArray;
+
+ void runInitialzersInImage(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+ void mirrorToOldAllImageInfos();
+ void garbageCollectImages();
+ void vmAccountingSetSuspended(bool suspend);
+ void copyCurrentGroupsNoLock(ImageGroupList& groups) const;
+
+ const BinaryClosure* _mainClosure = nullptr;
+ const void* _dyldCacheAddress = nullptr;
+ const char* _dyldCachePath = nullptr;
+ StartImageArray* _initialImages = nullptr;
+ const char* _mainExeOverridePath = nullptr;
+ _dyld_objc_notify_mapped _objcNotifyMapped = nullptr;
+ _dyld_objc_notify_init _objcNotifyInit = nullptr;
+ _dyld_objc_notify_unmapped _objcNotifyUnmapped = nullptr;
+ ProgramVars* _programVars = nullptr;
+ dyld_all_image_infos* _oldAllImageInfos = nullptr;
+ dyld_image_info* _oldAllImageArray = nullptr;
+ uint32_t _oldArrayAllocCount = 0;
+ pthread_mutex_t _initializerLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+ pthread_cond_t _initializerCondition= PTHREAD_COND_INITIALIZER;
+ int32_t _gcCount = 0;
+};
+
+extern AllImages gAllImages;
+
+
+} // dyld3
+
+
+#endif // __ALL_IMAGES_H__
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <assert.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+#include <bootstrap.h>
+
+#include "ClosureBuffer.h"
+#include "PathOverrides.h"
+
+
+namespace dyld3 {
+
+TypedContentBuffer::TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize)
+{
+ _size = elementsTotalSize + (elementsCount+1)*(sizeof(Element)+4); // worst case padding, plus "end" element
+ vm_address_t bufferAddress = 0;
+ assert(::vm_allocate(mach_task_self(), &bufferAddress, _size, VM_FLAGS_ANYWHERE) == 0);
+ _buffer = (Element*)bufferAddress;
+ _currentEnd = _buffer;
+ _readOnly = false;
+}
+
+void TypedContentBuffer::free()
+{
+ if ( _buffer != nullptr )
+ vm_deallocate(mach_task_self(), (long)_buffer, _size);
+ _buffer = nullptr;
+}
+
+void TypedContentBuffer::addItem(uint32_t k, const void* content, size_t len)
+{
+ assert(!_readOnly);
+ assert(((char*)_currentEnd + len) < ((char*)_buffer + _size));
+ _currentEnd->kind = k;
+ _currentEnd->contentLength = (uint32_t)len;
+ if ( len != 0 )
+ memmove(&(_currentEnd->content), content, len);
+ size_t delta = (sizeof(Element) + len + 3) & (-4);
+ _currentEnd = (Element*)((char*)_currentEnd + delta);
+}
+
+vm_address_t TypedContentBuffer::vmBuffer() const
+{
+ assert(_readOnly);
+ return (vm_address_t)_buffer;
+}
+
+uint32_t TypedContentBuffer::vmBufferSize() const
+{
+ assert(_readOnly);
+ return (uint32_t)_size;
+}
+
+void TypedContentBuffer::doneBuilding()
+{
+ _readOnly = true;
+}
+
+
+const TypedContentBuffer::Element* TypedContentBuffer::Element::next() const
+{
+ return (Element*)((char*)this + sizeof(Element) + ((contentLength + 3) & -4));
+}
+
+TypedContentBuffer::TypedContentBuffer(const void* buff, size_t buffSize)
+ : _size(buffSize), _buffer((Element*)buff), _currentEnd((Element*)((char*)buff+buffSize)), _readOnly(true)
+{
+}
+
+unsigned TypedContentBuffer::count(uint32_t kind) const
+{
+ assert(_readOnly);
+ unsigned count = 0;
+ for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
+ if ( e->kind == kind )
+ ++count;
+ }
+ return count;
+}
+
+void TypedContentBuffer::forEach(uint32_t kind, void (^callback)(const void* content, size_t length)) const
+{
+ assert(_readOnly);
+ for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
+ if ( e->kind == kind ) {
+ callback(&(e->content), e->contentLength);
+ }
+ }
+}
+
+#if !BUILDING_CLOSURED
+
+ClosureBuffer::ClosureBuffer(const CacheIdent& cacheIdent, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
+ : TypedContentBuffer(2 + envVars.envVarCount() + groups.count(), computeSize(path, groups, envVars))
+{
+ addItem(kindCacheIdent, &cacheIdent, sizeof(CacheIdent));
+ addItem(kindTargetPath, path, strlen(path)+1);
+ envVars.forEachEnvVar(^(const char* envVar) {
+ addItem(kindEnvVar, envVar, strlen(envVar)+1);
+ });
+ for (size_t i=0; i < groups.count(); ++i) {
+ launch_cache::ImageGroup group(groups[i]);
+ addItem(kindImageGroup, group.binaryData(), group.size());
+ }
+ addItem(kindEnd, nullptr, 0);
+ doneBuilding();
+}
+
+size_t ClosureBuffer::computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
+{
+ __block size_t result = sizeof(CacheIdent);
+ result += (strlen(path) + 1);
+ envVars.forEachEnvVar(^(const char* envVar) {
+ result += (strlen(envVar) + 1);
+ });
+ for (size_t i=0; i < groups.count(); ++i) {
+ launch_cache::ImageGroup group(groups[i]);
+ result += group.size();
+ }
+ return result;
+}
+
+#endif
+
+ClosureBuffer::ClosureBuffer(const char* errorMessage)
+ : TypedContentBuffer(1, strlen(errorMessage+2))
+{
+ addItem(kindErrorMessage, errorMessage, strlen(errorMessage)+1);
+ doneBuilding();
+}
+
+ClosureBuffer::ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroup)
+ : TypedContentBuffer(1, launch_cache::ImageGroup(imageGroup).size())
+{
+ addItem(kindImageGroup, imageGroup, launch_cache::ImageGroup(imageGroup).size());
+ doneBuilding();
+}
+
+ClosureBuffer::ClosureBuffer(const launch_cache::BinaryClosureData* closure)
+ : TypedContentBuffer(1, launch_cache::Closure(closure).size())
+{
+ addItem(kindClosure, closure, launch_cache::Closure(closure).size());
+ doneBuilding();
+}
+
+
+ClosureBuffer::ClosureBuffer(const void* buff, size_t buffSize)
+ : TypedContentBuffer(buff, buffSize)
+{
+}
+
+const ClosureBuffer::CacheIdent& ClosureBuffer::cacheIndent() const
+{
+ __block CacheIdent* ident = nullptr;
+ forEach(kindCacheIdent, ^(const void* content, size_t length) {
+ ident = (CacheIdent*)content;
+ assert(length == sizeof(CacheIdent));
+ });
+ assert(ident != nullptr);
+ return *ident;
+}
+
+const char* ClosureBuffer::targetPath() const
+{
+ __block char* path = nullptr;
+ forEach(kindTargetPath, ^(const void* content, size_t length) {
+ path = (char*)content;
+ });
+ assert(path != nullptr);
+ return path;
+}
+
+uint32_t ClosureBuffer::envVarCount() const
+{
+ __block uint32_t count = 0;
+ forEach(kindEnvVar, ^(const void* content, size_t length) {
+ ++count;
+ });
+ return count;
+}
+
+void ClosureBuffer::copyImageGroups(const char* envVars[]) const
+{
+ __block uint32_t index = 0;
+ forEach(kindEnvVar, ^(const void* content, size_t length) {
+ envVars[index] = (char*)content;
+ ++index;
+ });
+}
+
+uint32_t ClosureBuffer::imageGroupCount() const
+{
+ __block uint32_t count = 0;
+ forEach(kindImageGroup, ^(const void* content, size_t length) {
+ ++count;
+ });
+ return count;
+}
+
+void ClosureBuffer::copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const
+{
+ __block uint32_t index = 0;
+ forEach(kindImageGroup, ^(const void* content, size_t length) {
+ imageGroups[index] = (launch_cache::BinaryImageGroupData*)content;
+ ++index;
+ });
+}
+
+bool ClosureBuffer::isError() const
+{
+ return ( errorMessage() != nullptr );
+}
+
+const char* ClosureBuffer::errorMessage() const
+{
+ __block char* message = nullptr;
+ forEach(kindErrorMessage, ^(const void* content, size_t length) {
+ message = (char*)content;
+ });
+ return message;
+}
+
+const launch_cache::BinaryClosureData* ClosureBuffer::closure() const
+{
+ __block const launch_cache::BinaryClosureData* result = nullptr;
+ forEach(kindClosure, ^(const void* content, size_t length) {
+ result = (const launch_cache::BinaryClosureData*)content;
+ });
+ assert(result != nullptr);
+ return result;
+}
+
+
+const launch_cache::BinaryImageGroupData* ClosureBuffer::imageGroup() const
+{
+ __block const launch_cache::BinaryImageGroupData* result = nullptr;
+ forEach(kindImageGroup, ^(const void* content, size_t length) {
+ result = (const launch_cache::BinaryImageGroupData*)content;
+ });
+ assert(result != nullptr);
+ return result;
+}
+
+
+
+
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_CLOSURE_BUFFER_H__
+#define __DYLD_CLOSURE_BUFFER_H__
+
+#include "Logging.h"
+#include "LaunchCache.h"
+#include "PathOverrides.h"
+
+namespace dyld3 {
+
+
+// simple class for packing typed content into a vm_allocated buffer
+class VIS_HIDDEN TypedContentBuffer
+{
+public:
+ // buffer creation
+ TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize);
+ void addItem(uint32_t k, const void* content, size_t len);
+ void doneBuilding();
+ vm_address_t vmBuffer() const;
+ uint32_t vmBufferSize() const;
+
+ // buffer parsing
+ TypedContentBuffer(const void* buff, size_t buffSize);
+ unsigned count(uint32_t) const;
+ void forEach(uint32_t, void (^callback)(const void* content, size_t length)) const;
+
+ void free();
+
+private:
+ struct Element
+ {
+ uint32_t kind;
+ uint32_t contentLength;
+ uint8_t content[];
+
+ const Element* next() const;
+ };
+
+ size_t _size;
+ Element* _buffer;
+ Element* _currentEnd;
+ bool _readOnly;
+};
+
+
+class VIS_HIDDEN ClosureBuffer : public TypedContentBuffer
+{
+public:
+
+ struct CacheIdent
+ {
+ uint8_t cacheUUID[16];
+ uint64_t cacheAddress;
+ uint64_t cacheMappedSize;
+ };
+
+ // client creation
+ ClosureBuffer(const CacheIdent&, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars);
+
+ // closured creation
+ ClosureBuffer(const char* errorMessage);
+ ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroupResult);
+ ClosureBuffer(const launch_cache::BinaryClosureData* closureResult);
+
+ // client extraction
+ bool isError() const;
+ const char* errorMessage() const;
+ const launch_cache::BinaryClosureData* closure() const;
+ const launch_cache::BinaryImageGroupData* imageGroup() const;
+
+ // closure builder usage
+ ClosureBuffer(const void* buff, size_t buffSize);
+ const CacheIdent& cacheIndent() const;
+ const char* targetPath() const;
+ uint32_t envVarCount() const;
+ void copyImageGroups(const char* envVars[]) const;
+ uint32_t imageGroupCount() const;
+ void copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const;
+
+private:
+ enum { kindEnd=0, kindCacheIdent, kindTargetPath, kindEnvVar, kindImageGroup, kindClosure, kindErrorMessage };
+ static size_t computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars);
+
+};
+
+
+
+
+} // namespace dyld3
+
+#endif // __DYLD_CLOSURE_BUFFER_H__
--- /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_ALTERNATE_CODEDIRECTORIES = 0x1000,
+ CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5,
+ CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT =
+ CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX,
+ CSSLOT_CMS_SIGNATURE = 0x10000,
+
+ kSecCodeSignatureAdhoc = 2
+};
+
+enum {
+ CS_REQUIRE_LV = 0x0002000 // require library validation
+};
+
+//
+// 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)
+
+ char end_earliest[0];
+
+ /* Version 0x20100 */
+ uint32_t scatterOffset; /* offset of optional scatter vector */
+ char end_withScatter[0];
+
+ /* Version 0x20200 */
+ uint32_t teamOffset; /* offset of optional team identifier */
+ char end_withTeam[0];
+
+ /* Version 0x20300 */
+ uint32_t spare3; /* unused (must be zero) */
+ uint64_t codeLimit64; /* limit to main image signature range, 64 bits */
+ char end_withCodeLimit64[0];
+
+ /* Version 0x20400 */
+ uint64_t execSegBase; /* offset of executable segment */
+ uint64_t execSegLimit; /* limit of executable segment */
+ uint64_t execSegFlags; /* exec segment flags */
+ char end_withExecSeg[0];
+
+ /* 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
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <_simple.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/fat.h>
+#include <pthread.h>
+#include <libc_private.h>
+
+#include "Diagnostics.h"
+
+#if BUILDING_CACHE_BUILDER
+ #include <dispatch/dispatch.h>
+ dispatch_queue_t sWarningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", NULL);
+#endif
+
+Diagnostics::Diagnostics(bool verbose)
+#if !DYLD_IN_PROCESS
+ : _verbose(verbose)
+ , _prefix("")
+#endif
+{
+}
+
+#if !DYLD_IN_PROCESS
+Diagnostics::Diagnostics(const std::string& prefix, bool verbose)
+ : _verbose(verbose)
+ , _prefix(prefix)
+{
+}
+#endif
+
+Diagnostics::~Diagnostics()
+{
+ clearError();
+}
+
+void Diagnostics::error(const char* format, ...)
+{
+ _buffer = _simple_salloc();
+ va_list list;
+ va_start(list, format);
+ _simple_vsprintf(_buffer, format, list);
+ va_end(list);
+
+#if !DYLD_IN_PROCESS
+ if ( !_verbose )
+ return;
+
+ char *output_string;
+ va_start(list, format);
+ vasprintf(&output_string, format, list);
+ va_end(list);
+
+ if (_prefix.empty()) {
+ fprintf(stderr, "%s", output_string);
+ } else {
+ fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string);
+ }
+#endif
+}
+
+bool Diagnostics::hasError() const
+{
+ return _buffer != nullptr;
+}
+
+bool Diagnostics::noError() const
+{
+ return _buffer == nullptr;
+}
+
+void Diagnostics::clearError()
+{
+ if ( _buffer )
+ _simple_sfree(_buffer);
+ _buffer = nullptr;
+}
+
+void Diagnostics::assertNoError() const
+{
+ if ( _buffer != nullptr )
+ abort_report_np("%s", _simple_string(_buffer));
+}
+
+#if DYLD_IN_PROCESS
+const char* Diagnostics::errorMessage() const
+{
+ return _simple_string(_buffer);
+}
+
+#else
+void Diagnostics::warning(const char* format, ...)
+{
+ _SIMPLE_STRING tmp = _simple_salloc();
+ va_list list;
+ va_start(list, format);
+ _simple_vsprintf(tmp, format, list);
+ va_end(list);
+#if BUILDING_CACHE_BUILDER
+ dispatch_sync(sWarningQueue, ^{
+ _warnings.insert(_simple_string(tmp));
+ });
+#else
+ _warnings.insert(_simple_string(tmp));
+#endif
+ _simple_sfree(tmp);
+}
+
+void Diagnostics::verbose(const char* format, ...)
+{
+ if ( !_verbose )
+ return;
+
+ char* output_string;
+ va_list list;
+ va_start(list, format);
+ vasprintf(&output_string, format, list);
+ va_end(list);
+
+ if (_prefix.empty()) {
+ fprintf(stderr, "%s", output_string);
+ } else {
+ fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string);
+ }
+}
+
+const std::string Diagnostics::prefix() const
+{
+ return _prefix;
+}
+
+void Diagnostics::copy(const Diagnostics& other)
+{
+ if ( other.hasError() )
+ error("%s", other.errorMessage().c_str());
+ for (const std::string& warn : other.warnings())
+ warning("%s", warn.c_str());
+}
+
+std::string Diagnostics::errorMessage() const
+{
+ if ( _buffer != nullptr )
+ return _simple_string(_buffer);
+ else
+ return std::string();
+}
+
+const std::set<std::string> Diagnostics::warnings() const
+{
+#if BUILDING_CACHE_BUILDER
+ __block std::set<std::string> retval;
+ dispatch_sync(sWarningQueue, ^{
+ retval = _warnings;
+ });
+ return retval;
+#else
+ return _warnings;
+#endif
+}
+
+void Diagnostics::clearWarnings()
+{
+#if BUILDING_CACHE_BUILDER
+ dispatch_sync(sWarningQueue, ^{
+ _warnings.clear();
+ });
+#else
+ _warnings.clear();
+#endif
+}
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 Diagnostics_h
+#define Diagnostics_h
+
+#include <stdint.h>
+
+#if !DYLD_IN_PROCESS
+#include <set>
+#include <string>
+#include <vector>
+#include <dispatch/dispatch.h>
+#endif
+
+#include "Logging.h"
+
+
+class VIS_HIDDEN Diagnostics
+{
+public:
+ Diagnostics(bool verbose=false);
+ ~Diagnostics();
+
+ void error(const char* format, ...) __attribute__((format(printf, 2, 3)));
+#if !DYLD_IN_PROCESS
+ Diagnostics(const std::string& prefix, bool verbose=false);
+
+ void warning(const char* format, ...) __attribute__((format(printf, 2, 3)));
+ void verbose(const char* format, ...) __attribute__((format(printf, 2, 3)));
+ void copy(const Diagnostics&);
+#endif
+
+ bool hasError() const;
+ bool noError() const;
+ void clearError();
+ void assertNoError() const;
+
+#if DYLD_IN_PROCESS
+ const char* errorMessage() const;
+#else
+ const std::string prefix() const;
+ std::string errorMessage() const;
+ const std::set<std::string> warnings() const;
+ void clearWarnings();
+#endif
+
+private:
+ void* _buffer = nullptr;
+#if !DYLD_IN_PROCESS
+ std::string _prefix;
+ std::set<std::string> _warnings;
+ bool _verbose = false;
+#endif
+};
+
+
+
+
+#endif // Diagnostics_h
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+
+#include "DyldCacheParser.h"
+#include "Trie.hpp"
+
+
+namespace dyld3 {
+
+DyldCacheParser::DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile)
+{
+ _data = (long)cacheHeader;
+ if ( rawFile )
+ _data |= 1;
+}
+
+const dyld_cache_header* DyldCacheParser::header() const
+{
+ return (dyld_cache_header*)(_data & -2);
+}
+
+const DyldSharedCache* DyldCacheParser::cacheHeader() const
+{
+ return (DyldSharedCache*)header();
+}
+
+bool DyldCacheParser::cacheIsMappedRaw() const
+{
+ return (_data & 1);
+}
+
+
+uint64_t DyldCacheParser::dataRegionRuntimeVmOffset() const
+{
+ const dyld_cache_header* cacheHeader = header();
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+ return (mappings[1].address - mappings[0].address);
+}
+
+const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::cachedDylibsGroup() const
+{
+ const dyld_cache_header* cacheHeader = header();
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+ if ( cacheIsMappedRaw() ) {
+ // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup
+ uint64_t offsetInLinkEditRegion = (cacheHeader->dylibsImageGroupAddr - mappings[2].address);
+ return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion);
+ }
+ else {
+ // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find ImageGroup
+ return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->dylibsImageGroupAddr - mappings[0].address));
+ }
+}
+
+
+const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::otherDylibsGroup() const
+{
+ const dyld_cache_header* cacheHeader = header();
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+ if ( cacheIsMappedRaw() ) {
+ // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup
+ uint64_t offsetInLinkEditRegion = (cacheHeader->otherImageGroupAddr - mappings[2].address);
+ return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion);
+ }
+ else {
+ // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find ImageGroup
+ return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->otherImageGroupAddr - mappings[0].address));
+ }
+}
+
+const dyld3::launch_cache::binary_format::Closure* DyldCacheParser::findClosure(const char* path) const
+{
+ const dyld_cache_header* cacheHeader = header();
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+ const uint8_t* executableTrieStart = nullptr;
+ const uint8_t* executableTrieEnd = nullptr;
+ const uint8_t* closuresStart = nullptr;
+
+ if ( cacheIsMappedRaw() ) {
+ // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures
+ executableTrieStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset;
+ executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize;
+ closuresStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset;
+ }
+ else {
+ // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find trie and closures
+ uintptr_t slide = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address);
+ executableTrieStart = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide);
+ executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize;
+ closuresStart = (uint8_t*)(cacheHeader->progClosuresAddr + slide);
+ }
+ Diagnostics diag;
+ const uint8_t* imageNode = dyld3::MachOParser::trieWalk(diag, executableTrieStart, executableTrieEnd, path);
+ if ( imageNode != NULL ) {
+ uint32_t closureOffset = (uint32_t)dyld3::MachOParser::read_uleb128(diag, imageNode, executableTrieEnd);
+ return (const dyld3::launch_cache::BinaryClosureData*)((uint8_t*)closuresStart + closureOffset);
+ }
+ return nullptr;
+}
+
+
+#if !DYLD_IN_PROCESS
+void DyldCacheParser::forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* cls)) const
+{
+ const dyld_cache_header* cacheHeader = header();
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+ const uint8_t* executableTrieStart = nullptr;
+ const uint8_t* executableTrieEnd = nullptr;
+ const uint8_t* closuresStart = nullptr;
+
+ if ( cacheIsMappedRaw() ) {
+ // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures
+ executableTrieStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset;
+ executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize;
+ closuresStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset;
+ }
+ else {
+ // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find trie and closures
+ uintptr_t slide = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address);
+ executableTrieStart = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide);
+ executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize;
+ closuresStart = (uint8_t*)(cacheHeader->progClosuresAddr + slide);
+ }
+
+ std::vector<DylibIndexTrie::Entry> closureEntries;
+ if ( Trie<DylibIndex>::parseTrie(executableTrieStart, executableTrieEnd, closureEntries) ) {
+ for (DylibIndexTrie::Entry& entry : closureEntries ) {
+ uint32_t offset = entry.info.index;
+ if ( offset < cacheHeader->progClosuresSize )
+ handler(entry.name.c_str(), (const dyld3::launch_cache::binary_format::Closure*)(closuresStart+offset));
+ }
+ }
+}
+#endif
+
+
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 DyldCacheParser_h
+#define DyldCacheParser_h
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "LaunchCacheFormat.h"
+
+namespace dyld3 {
+
+class VIS_HIDDEN DyldCacheParser
+{
+public:
+#if !DYLD_IN_PROCESS
+ static bool isValidDyldCache(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables);
+#endif
+
+ DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile);
+ const DyldSharedCache* cacheHeader() const;
+ bool cacheIsMappedRaw() const;
+
+
+
+ //
+ // Get ImageGroup for cached dylibs built into this cache files
+ //
+ const dyld3::launch_cache::binary_format::ImageGroup* cachedDylibsGroup() const;
+
+
+ //
+ // Get ImageGroup for other OS dylibs and bundles built into this cache files
+ //
+ const dyld3::launch_cache::binary_format::ImageGroup* otherDylibsGroup() const;
+
+
+ //
+ // returns closure for given path, or nullptr if no closure found
+ //
+ const dyld3::launch_cache::binary_format::Closure* findClosure(const char* path) const;
+
+ //
+ // returns what vmOffset of data (r/w) region from cache header will be when cache is used in a process
+ //
+ uint64_t dataRegionRuntimeVmOffset() const;
+
+#if !DYLD_IN_PROCESS
+ //
+ // Iterates over closure of OS programs built into shared cache
+ //
+ void forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure*)) const;
+#endif
+
+private:
+ const dyld_cache_header* header() const;
+
+ long _data; // low bit means rawFile
+};
+
+} // namespace dyld3
+
+#endif // DyldCacheParser_h
--- /dev/null
+/*
+ * Copyright (c) 2017 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 LaunchCache_h
+#define LaunchCache_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#if !DYLD_IN_PROCESS
+ #include <vector>
+ #include <unordered_set>
+ #include <string>
+ #include "shared-cache/DyldSharedCache.h"
+#endif
+
+#include "Diagnostics.h"
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+
+namespace dyld3 {
+
+class DyldCacheParser;
+
+namespace launch_cache {
+
+
+namespace binary_format {
+ struct Image;
+ struct ImageGroup;
+ union ImageRef;
+ struct Closure;
+ struct DiskImage;
+ struct CachedImage;
+ struct AllFixupsBySegment;
+ struct SegmentFixupsByPage;
+}
+
+typedef binary_format::Image BinaryImageData;
+typedef binary_format::ImageGroup BinaryImageGroupData;
+typedef binary_format::Closure BinaryClosureData;
+
+
+struct VIS_HIDDEN MemoryRange
+{
+ bool contains(const MemoryRange& other) const;
+ bool intersects(const MemoryRange& other) const;
+
+ const void* address;
+ uint64_t size;
+};
+
+
+class VIS_HIDDEN SlowLoadSet
+{
+public:
+ SlowLoadSet(const BinaryImageData** start, const BinaryImageData** end) : _start(start), _end(end), _current(start) { }
+ bool contains(const BinaryImageData*);
+ bool add(const BinaryImageData*);
+ void forEach(void (^handler)(const BinaryImageData*));
+ void forEach(void (^handler)(const BinaryImageData*, bool& stop));
+ long count() const;
+private:
+ const BinaryImageData** const _start;
+ const BinaryImageData** const _end;
+ const BinaryImageData** _current;
+};
+
+struct ImageGroup;
+
+
+template <typename T>
+class VIS_HIDDEN DynArray
+{
+public:
+ DynArray(uintptr_t count, T* storage) : _count(count), _elements(storage) { }
+#if !DYLD_IN_PROCESS
+ DynArray(const std::vector<T>& vec) : _count(vec.size()), _elements((T*)&vec[0]) { }
+#endif
+
+ T& operator[](size_t idx) { assert(idx < _count); return _elements[idx]; }
+ const T& operator[](size_t idx) const { assert(idx < _count); return _elements[idx]; }
+ uintptr_t count() const { return _count; }
+private:
+ uintptr_t _count;
+ T* _elements;
+};
+
+
+// STACK_ALLOC_DYNARRAY(foo, 10, myarray);
+#define STACK_ALLOC_DYNARRAY(_type, _count, _name) \
+ uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+ dyld3::launch_cache::DynArray<_type> _name(_count, (_type*)__##_name##_array_alloc);
+
+
+typedef DynArray<const BinaryImageGroupData*> ImageGroupList;
+
+
+// In the pre-computed fixups for an Image, each fixup location is set to a TargetSymbolValue
+// which is an abstract encoding of a resolved symbol in an image that can be turned into a
+// real address once all ASLR slides are known.
+struct VIS_HIDDEN TargetSymbolValue
+{
+#if DYLD_IN_PROCESS
+ class LoadedImages
+ {
+ public:
+ virtual const uint8_t* dyldCacheLoadAddressForImage() = 0;
+ virtual const mach_header* loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup) = 0;
+ virtual void forEachImage(void (^handler)(uint32_t anIndex, const BinaryImageData*, const mach_header*, bool& stop)) = 0;
+ virtual void setAsNeverUnload(uint32_t anIndex) = 0;
+ };
+
+ uintptr_t resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const;
+#else
+ static TargetSymbolValue makeInvalid();
+ static TargetSymbolValue makeAbsolute(uint64_t value);
+ static TargetSymbolValue makeSharedCacheOffset(uint32_t offset);
+ static TargetSymbolValue makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum);
+ static TargetSymbolValue makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport);
+ std::string asString(ImageGroup group) const;
+ bool operator==(const TargetSymbolValue& other) const { return (_data.raw == other._data.raw); }
+ bool isSharedCacheTarget(uint64_t& offsetInCache) const;
+ bool isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const;
+ bool isInvalid() const;
+#endif
+private:
+ TargetSymbolValue();
+
+ enum Kinds { kindSharedCache, kindAbsolute, kindGroup, kindDynamicGroup };
+
+
+ struct SharedCacheOffsetTarget {
+ uint64_t kind : 2, // kindSharedCache
+ offsetIntoCache : 62;
+ };
+ struct AbsoluteTarget {
+ uint64_t kind : 2, // kindAbsolute
+ value : 62;
+ };
+ struct GroupImageTarget {
+ uint64_t kind : 2, // kindGroup
+ isIndirectGroup : 1, // 0 => use groupNum directly. 1 => index indirect side table
+ groupNum : 7, // 0 not used, 1 => other dylibs, 2 => main closure, 3 => first dlopen group
+ indexInGroup : 12,
+ offsetInImage : 42;
+ };
+ struct DynamicGroupImageTarget {
+ uint64_t kind : 2, // kindDynamicGroup
+ weakImport : 1,
+ imagePathOffset : 30,
+ symbolNameOffset: 31;
+ };
+ union {
+ SharedCacheOffsetTarget sharedCache;
+ AbsoluteTarget absolute;
+ GroupImageTarget group;
+ DynamicGroupImageTarget dynamicGroup;
+ uint64_t raw;
+ } _data;
+
+ static_assert(sizeof(_data) == 8, "Overflow in size of TargetSymbolValue");
+};
+
+
+struct VIS_HIDDEN Image
+{
+ enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 };
+ enum class FixupKind { rebase32, rebase64, bind32, bind64, rebaseText32, bindText32, bindTextRel32, bindImportJmp32 };
+
+ Image(const BinaryImageData* binaryData) : _binaryData(binaryData) { }
+
+ bool valid() const { return (_binaryData != nullptr); }
+ const BinaryImageData* binaryData() const { return _binaryData; }
+ const ImageGroup group() const;
+ uint32_t maxLoadCount() const;
+ const char* path() const;
+ const char* leafName() const;
+ uint32_t pathHash() const;
+ const uuid_t* uuid() const;
+ bool isInvalid() const;
+ bool hasObjC() const;
+ bool isBundle() const;
+ bool hasWeakDefs() const;
+ bool mayHavePlusLoads() const;
+ bool hasTextRelocs() const;
+ bool neverUnload() const;
+ bool cwdMustBeThisDir() const;
+ bool isPlatformBinary() const;
+ bool overridableDylib() const;
+ bool validateUsingModTimeAndInode() const;
+ bool validateUsingCdHash() const;
+ uint64_t fileModTime() const;
+ uint64_t fileINode() const;
+ const uint8_t* cdHash16() const;
+ void forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const;
+#if !DYLD_IN_PROCESS
+ bool recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set<const BinaryImageData*>& allDependents) const;
+#endif
+ bool recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents,
+ void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const;
+ bool containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const;
+ bool segmentHasFixups(uint32_t segIndex) const;
+ void forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+ void forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const;
+ void forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const;
+ void forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+
+ bool isDiskImage() const;
+
+ // the following are only valid if isDiskImage() returns false
+ const binary_format::CachedImage* asCachedImage() const;
+ uint32_t cacheOffset() const;
+ uint32_t patchStartIndex() const;
+ uint32_t patchCount() const;
+
+
+ // the following are only valid if isDiskImage() returns true
+ const binary_format::DiskImage* asDiskImage() const;
+ uint64_t sliceOffsetInFile() const;
+ bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+ bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
+ uint64_t vmSizeToMap() const;
+ void forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+ void forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+ void forEachFixup(uint32_t segIndex, MemoryRange segContent,
+ void (^handler)(uint64_t segOffset, FixupKind kind, TargetSymbolValue value, bool& stop)) const;
+
+#if !DYLD_IN_PROCESS
+ void printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const;
+#endif
+
+// todo: fairPlayTextPages
+
+private:
+ friend struct ImageGroup;
+ friend struct Closure;
+
+ bool recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped,
+ void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const;
+ uint32_t pageSize() const;
+ const binary_format::SegmentFixupsByPage* segmentFixups(uint32_t segIndex) const;
+ static void forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal,
+ void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop));
+ static Image resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides=true);
+
+
+ const BinaryImageData* _binaryData;
+};
+
+
+struct VIS_HIDDEN ImageGroup
+{
+ ImageGroup(const BinaryImageGroupData* binaryData) : _binaryData(binaryData) { }
+
+ size_t size() const;
+ uint32_t imageCount() const;
+ uint32_t groupNum() const;
+ bool dylibsExpectedOnDisk() const;
+ const Image image(uint32_t index) const;
+ uint32_t indexInGroup(const BinaryImageData* image) const;
+ const BinaryImageData* findImageByPath(const char* path, uint32_t& foundIndex) const;
+ const BinaryImageData* findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const;
+ const BinaryImageData* imageBinary(uint32_t index) const;
+ binary_format::ImageRef dependentPool(uint32_t index) const;
+ const BinaryImageGroupData* binaryData() const { return _binaryData; }
+ const char* stringFromPool(uint32_t offset) const;
+ uint32_t indirectGroupNum(uint32_t index) const;
+ void forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDyilbRef, bool& stop)) const;
+ void forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDyilb, bool& stop)) const;
+ void forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const;
+#if DYLD_IN_PROCESS
+ void forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const;
+ void forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex,
+ void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool& stop)) const;
+#else
+ void forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const;
+ void forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector<uint32_t>& usesPointersCacheOffsets, bool& stop)) const;
+ bool hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& index) const;
+#endif
+
+ static uint32_t hashFunction(const char* s);
+#if !DYLD_IN_PROCESS
+ void printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const;
+ void printStatistics(FILE* out=stderr) const;
+#endif
+
+private:
+ friend struct Image;
+
+ const char* stringPool() const;
+ uint32_t stringPoolSize() const;
+ const uint64_t* segmentPool(uint32_t index) const;
+ const binary_format::AllFixupsBySegment* fixUps(uint32_t offset) const;
+ const TargetSymbolValue* targetValuesArray() const;
+ uint32_t targetValuesCount() const;
+ uint32_t initializersPoolCount() const;
+ const uint32_t* initializerOffsetsPool() const;
+ const uint32_t initializerOffsetsCount() const;
+ const binary_format::ImageRef* intializerListPool() const;
+ const uint32_t intializerListPoolCount() const;
+ const uint32_t* dofOffsetsPool() const;
+ const uint32_t dofOffsetsCount() const;
+ const uint32_t* indirectGroupNumsPool() const;
+ const uint32_t indirectGroupNumsCount() const;
+ void forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset,
+ void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const;
+
+ const BinaryImageGroupData* _binaryData;
+};
+
+
+
+struct VIS_HIDDEN Closure
+{
+ Closure(const BinaryClosureData* binaryData);
+
+ size_t size() const;
+ const uuid_t* dyldCacheUUID() const;
+ const uint8_t* cdHash() const;
+ uint32_t initialImageCount() const;
+ uint32_t mainExecutableImageIndex() const;
+ uint32_t mainExecutableEntryOffset() const;
+ bool mainExecutableUsesCRT() const;
+ bool isRestricted() const;
+ bool usesLibraryValidation() const;
+ const BinaryImageData* libSystem(const ImageGroupList& groups);
+ const BinaryImageData* libDyld(const ImageGroupList& groups);
+ uint32_t libdyldVectorOffset() const;
+ const ImageGroup group() const;
+ const BinaryClosureData* binaryData() const { return _binaryData; }
+ void forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const;
+ void forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
+
+#if !DYLD_IN_PROCESS
+ void printAsJSON(const ImageGroupList& groupList, bool printFixups=true, bool printDependentsDetails=false, FILE* out=stdout) const;
+ void printStatistics(FILE* out=stderr) const;
+#endif
+
+private:
+ const BinaryClosureData* _binaryData;
+};
+
+
+
+
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+
+#endif // LaunchCache_h
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 LaunchCacheFormat_h
+#define LaunchCacheFormat_h
+
+
+#include <stdint.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+
+#include "LaunchCache.h"
+
+
+namespace dyld3 {
+namespace launch_cache {
+namespace binary_format {
+
+
+// bump this number each time binary format changes
+enum { kFormatVersion = 8 };
+
+union VIS_HIDDEN ImageRef {
+ ImageRef() : val(0xFFFFFFFF) { }
+ ImageRef(uint8_t kind, uint32_t groupNum, uint32_t indexInGroup) : _linkKind(kind), _groupNum(groupNum), _indexInGroup(indexInGroup) {
+ assert(groupNum < (1 << 18));
+ assert(indexInGroup < (1 << 12));
+ }
+ uint8_t kind() const { return _linkKind; }
+ uint32_t groupNum() const { return _groupNum; }
+ uint32_t indexInGroup() const { return _indexInGroup; }
+ uint16_t value() const { return val; }
+ void clearKind() { _linkKind = 0; }
+
+ bool operator==(const ImageRef& rhs) const {
+ return (val == rhs.val);
+ }
+ bool operator!=(const ImageRef& rhs) const {
+ return (val != rhs.val);
+ }
+ static ImageRef weakImportMissing();
+ static ImageRef makeEmptyImageRef() { return ImageRef(); }
+
+private:
+ ImageRef(uint32_t v) : val(v) { }
+
+ uint32_t val;
+ struct {
+ uint32_t _linkKind : 2, // Image::LinkKind
+ _groupNum : 18, // 0 => cached dylib group, 1 => other dylib group, 2 => main closure, etc
+ _indexInGroup : 12; // max 64K images in group
+ };
+};
+
+
+
+
+// In disk based images, all segments are multiples of page size
+// This struct just tracks the size (disk and vm) of each segment.
+// This is compact for most every image which have contiguous segments.
+// If the image does not have contiguous segments (rare), an extra
+// DiskSegment is inserted with the paddingNotSeg bit set.
+struct DiskSegment
+{
+ uint64_t filePageCount : 30,
+ vmPageCount : 30,
+ permissions : 3,
+ paddingNotSeg : 1;
+};
+
+
+// In cache DATA_DIRTY is not page aligned or sized
+// This struct allows segments with any alignment and up to 256MB in size
+struct DyldCacheSegment
+{
+ uint64_t cacheOffset : 32,
+ size : 28,
+ permissions : 4;
+};
+
+// When an Image is built on the device, the mtime and inode are recorded.
+// When built off device, the first 16 bytes of SHA1 of CodeDirectory is recorded.
+union FileInfo
+{
+ struct {
+ uint64_t mtime;
+ uint64_t inode;
+ } statInfo;
+ struct {
+ uint8_t bytes[16];
+ } cdHash16;
+};
+
+struct Image
+{
+ uint32_t isDiskImage : 1, // images are DiskImage - not Image
+ isInvalid : 1, // an error occurred creating the info for this image
+ has16KBpages : 1,
+ hasTextRelocs : 1,
+ hasObjC : 1,
+ mayHavePlusLoads : 1,
+ isEncrypted : 1, // image is DSMOS or FairPlay encrypted
+ hasWeakDefs : 1,
+ neverUnload : 1,
+ cwdSameAsThis : 1, // dylibs use file system relative paths, cwd must be main's dir
+ isPlatformBinary : 1, // part of OS - can be loaded into LV process
+ isBundle : 1,
+ overridableDylib : 1, // only applicable to group 0
+ padding : 7,
+ maxLoadCount : 12;
+ int32_t groupOffset; // back pointer to containing ImageGroup (from start of Image)
+ uint32_t pathPoolOffset;
+ uint32_t pathHash;
+ FileInfo fileInfo;
+ uuid_t uuid;
+ uint16_t dependentsArrayStartIndex;
+ uint16_t dependentsArrayCount;
+ uint16_t segmentsArrayStartIndex;
+ uint16_t segmentsArrayCount;
+ uint16_t initBeforeArrayStartIndex;
+ uint16_t initBeforeArrayCount;
+ uint16_t initOffsetsArrayStartIndex;
+ uint16_t initOffsetsArrayCount;
+ uint16_t dofOffsetsArrayStartIndex;
+ uint16_t dofOffsetsArrayCount;
+};
+
+// an image in the dyld shared cache
+struct CachedImage : public Image
+{
+ uint32_t patchStartIndex;
+ uint32_t patchCount;
+};
+
+// an image not in the dyld shared cache (loaded from disk at runtime)
+struct DiskImage : public Image
+{
+ uint32_t totalVmPages;
+ uint32_t sliceOffsetIn4K;
+ uint32_t codeSignFileOffset;
+ uint32_t codeSignFileSize;
+ uint32_t fixupsPoolOffset : 28, // offset in ImageGroup's pool for AllFixupsBySegment
+ fixupsPoolSegCount : 4; // count of segments in AllFixupsBySegment for this image
+ uint32_t fairPlayTextPageCount : 28,
+ fairPlayTextStartPage : 4;
+ uint32_t targetsArrayStartIndex; // index in ImageGroup's pool of OrdinalEntry
+ uint32_t targetsArrayCount;
+};
+
+
+// if an Image has an alias (symlink to it), the Image does not record the alias, but the ImageGroup does
+struct AliasEntry
+{
+ uint32_t aliasHash;
+ uint32_t imageIndexInGroup;
+ uint32_t aliasOffsetInStringPool;
+};
+
+// each DiskImage points to an array of these, one per segment with fixups
+struct AllFixupsBySegment
+{
+ uint32_t segIndex : 4,
+ offset : 28; // from start of AllFixupsBySegment to this seg's SegmentFixupsByPage
+};
+
+
+// This struct is suitable for passing into kernel when kernel supports fixups on page-in.
+struct SegmentFixupsByPage
+{
+ uint32_t size; // of this struct, including fixup opcodes
+ uint32_t pageSize; // 0x1000 or 0x4000
+ uint32_t pageCount;
+ uint32_t pageInfoOffsets[1]; // array size is pageCount
+ // each page info is a FixUpOpcode[]
+};
+
+enum class FixUpOpcode : uint8_t {
+ done = 0x00,
+// apply = 0x10,
+ rebase32 = 0x10, // add32 slide at current pageOffset, increment pageOffset by 4
+ rebase64 = 0x11, // add64 slide at current pageOffset, increment pageOffset by 8
+ bind32 = 0x12, // set 32-bit ordinal value at current pageOffset, increment pageOffset by 4
+ bind64 = 0x13, // set 64-bit ordinal value at current pageOffset, increment pageOffset by 8
+ rebaseText32 = 0x14, // add32 slide at current text pageOffset, increment pageOffset by 4
+ bindText32 = 0x15, // set 32-bit ordinal value at current text pageOffset, increment pageOffset by 4
+ bindTextRel32 = 0x16, // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 CALL to dylib)
+ bindImportJmp32 = 0x17, // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 JMP to dylib)
+// fixupChain64 = 0x18, // current page offset is start of a chain of locations to fix up
+// adjPageOffset = 0x20,
+ setPageOffset = 0x20, // low 4-bits is amount to increment (1 to 15). If zero, then add next ULEB (note: can set offset for unaligned pointer)
+ incPageOffset = 0x30, // low 4-bits *4 is amount to increment (4 to 60). If zero, then add next ULEB * 4
+// adjOrdinal = 0x40,
+ setOrdinal = 0x40, // low 4-bits is ordinal (1-15). If zero, then ordinal is next ULEB
+ incOrdinal = 0x50, // low 4-bits is ordinal inc amount (1-15). If zero, then ordinal is next ULEB
+ repeat = 0x60 // low 5-bits is how many next bytes to repeat. next ULEB is repeat count
+};
+
+// If a closure uses DYLD_LIBRARY_PATH to override an OS dylib, there is an
+// ImageRefOverride entry to redirect uses of the OS dylib.
+struct ImageRefOverride
+{
+ ImageRef standardDylib;
+ ImageRef overrideDylib;
+};
+
+// If a closure interposes on, or has a dylib that overrides, something in the dyld shared cache,
+// then closure's ImageGroup contains an array of these
+struct DyldCacheOverride
+{
+ uint64_t patchTableIndex : 24, // index into PatchTable array of group 0
+ imageIndex : 8, // index in this group (2) of what to replace with
+ imageOffset : 32; // offset within image to override something in cache
+};
+
+
+// The ImageGroup for the dyld shared cache dylibs contains and array of these
+// with one entry for each symbol in a cached dylib that is used by some other cached dylib.
+struct PatchTable
+{
+ uint32_t targetCacheOffset; // delta from the base address of the cache to the address of the symbol to patch
+ uint32_t offsetsStartIndex; // index into the PatchOffset array of first location to patch, last offset has low bit set
+};
+
+struct PatchOffset
+{
+ uint32_t last : 1,
+ hasAddend : 1,
+ dataRegionOffset : 30;
+};
+
+struct ImageGroup
+{
+ uint32_t imagesEntrySize : 8,
+ dylibsExpectedOnDisk : 1,
+ imageFileInfoIsCdHash : 1,
+ padding : 14;
+ uint32_t groupNum;
+ uint32_t imagesPoolCount;
+ uint32_t imagesPoolOffset; // offset to array of Image or DiskImage
+ uint32_t imageAliasCount;
+ uint32_t imageAliasOffset; // offset to array of AliasEntry
+ uint32_t segmentsPoolCount;
+ uint32_t segmentsPoolOffset; // offset to array of Segment or DyldCacheSegment
+ uint32_t dependentsPoolCount;
+ uint32_t dependentsPoolOffset; // offset to array of ImageRef
+ uint32_t intializerOffsetPoolCount;
+ uint32_t intializerOffsetPoolOffset; // offset to array of uint32_t
+ uint32_t intializerListPoolCount;
+ uint32_t intializerListPoolOffset; // offset to array of ImageRef
+ uint32_t targetsPoolCount;
+ uint32_t targetsOffset; // offset to array of TargetSymbolValue
+ uint32_t fixupsPoolSize;
+ uint32_t fixupsOffset; // offset to list of AllFixupsBySegment
+ uint32_t cachePatchTableCount;
+ uint32_t cachePatchTableOffset; // offset to array of PatchTable (used only in group 0)
+ uint32_t cachePatchOffsetsCount;
+ uint32_t cachePatchOffsetsOffset; // offset to array of PatchOffset cache offsets (used only in group 0)
+ uint32_t symbolOverrideTableCount;
+ uint32_t symbolOverrideTableOffset; // offset to array of DyldCacheOverride (used only in group 2)
+ uint32_t imageOverrideTableCount;
+ uint32_t imageOverrideTableOffset; // offset to array of ImageRefOverride (used only in group 2)
+ uint32_t dofOffsetPoolCount;
+ uint32_t dofOffsetPoolOffset; // offset to array of uint32_t
+ uint32_t indirectGroupNumPoolCount;
+ uint32_t indirectGroupNumPoolOffset; // offset to array of uint32_t
+ uint32_t stringsPoolSize;
+ uint32_t stringsPoolOffset;
+ // Image array
+ // Alias array
+ // Segment array
+ // ImageRef array
+ // Initializer offsets array
+ // Initializer ImageRef array
+ // TargetSymbolValue array
+ // AllFixupsBySegment pool
+ // PatchTable array
+ // PatchOffset array
+ // DyldCacheOverride array
+ // ImageRefOverride array
+ // string pool
+ // DOF offsets array
+};
+
+
+struct Closure
+{
+ enum { magicV1 = 0x31646c6e };
+
+ uint32_t magic;
+ uint32_t usesCRT : 1,
+ isRestricted : 1,
+ usesLibraryValidation : 1,
+ padding : 29;
+ uint32_t missingFileComponentsOffset; // offset to array of 16-bit string pool offset of path components
+ uint32_t dyldEnvVarsOffset;
+ uint32_t dyldEnvVarsCount;
+ uint32_t stringPoolOffset;
+ uint32_t stringPoolSize;
+ ImageRef libSystemRef;
+ ImageRef libDyldRef;
+ uint32_t libdyldVectorOffset;
+ uint32_t mainExecutableIndexInGroup;
+ uint32_t mainExecutableEntryOffset;
+ uint32_t initialImageCount;
+ uuid_t dyldCacheUUID; // all zero if this closure is embedded in a dyld cache
+ uint8_t mainExecutableCdHash[20]; // or UUID if not code signed
+ ImageGroup group;
+ // MissingFile array
+ // env vars array
+ // string pool
+};
+
+
+
+} // namespace binary_format
+
+} // namespace launch_cache
+} // namespace dyld
+
+
+#endif // LaunchCacheFormat_h
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+
+#if !DYLD_IN_PROCESS
+
+namespace dyld3 {
+namespace launch_cache {
+
+struct Node
+{
+ std::string value;
+ std::map<std::string, Node> map;
+ std::vector<Node> array;
+};
+
+static std::string hex(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%llX", value);
+ return buff;
+}
+
+static std::string hex5(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%05llX", value);
+ return buff;
+}
+
+static std::string decimal(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "%llu", value);
+ return buff;
+}
+
+static Node buildImageNode(const Image& image, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+ __block Node imageNode;
+
+ if ( image.isInvalid() )
+ return imageNode;
+
+ const ImageGroup group = image.group();
+ imageNode.map["path"].value = image.path();
+ __block Node imageAliases;
+ group.forEachAliasOf(group.indexInGroup(image.binaryData()), ^(const char* aliasPath, uint32_t aliasPathHash, bool& stop) {
+ Node anAlias;
+ anAlias.value = aliasPath;
+ imageAliases.array.push_back(anAlias);
+ });
+ if ( !imageAliases.array.empty() )
+ imageNode.map["aliases"] = imageAliases;
+ uuid_string_t uuidStr;
+ uuid_unparse(*image.uuid(), uuidStr);
+ imageNode.map["uuid"].value = uuidStr;
+ imageNode.map["has-objc"].value = (image.hasObjC() ? "true" : "false");
+ imageNode.map["has-weak-defs"].value = (image.hasWeakDefs() ? "true" : "false");
+ imageNode.map["never-unload"].value = (image.neverUnload() ? "true" : "false");
+ imageNode.map["platform-binary"].value = (image.isPlatformBinary() ? "true" : "false");
+ if ( group.groupNum() == 0 )
+ imageNode.map["overridable-dylib"].value = (image.overridableDylib() ? "true" : "false");
+ if ( image.cwdMustBeThisDir() )
+ imageNode.map["cwd-must-be-this-dir"].value = "true";
+ if ( image.isDiskImage() ) {
+ uint32_t csFileOffset;
+ uint32_t csSize;
+ if ( image.hasCodeSignature(csFileOffset, csSize) ) {
+ imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset);
+ imageNode.map["code-sign-location"].map["size"].value = hex(csSize);
+ }
+ uint32_t fpTextOffset;
+ uint32_t fpSize;
+ if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
+ imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
+ imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
+ }
+ if ( image.validateUsingModTimeAndInode() ) {
+ imageNode.map["file-mod-time"].value = hex(image.fileModTime());
+ imageNode.map["file-inode"].value = hex(image.fileINode());
+ }
+ else {
+ const uint8_t* cdHash = image.cdHash16();
+ std::string cdHashStr;
+ cdHashStr.reserve(32);
+ for (int j=0; j < 16; ++j) {
+ uint8_t byte = cdHash[j];
+ uint8_t nibbleL = byte & 0x0F;
+ uint8_t nibbleH = byte >> 4;
+ if ( nibbleH < 10 )
+ cdHashStr += '0' + nibbleH;
+ else
+ cdHashStr += 'a' + (nibbleH-10);
+ if ( nibbleL < 10 )
+ cdHashStr += '0' + nibbleL;
+ else
+ cdHashStr += 'a' + (nibbleL-10);
+ }
+ imageNode.map["file-cd-hash-16"].value = cdHashStr;
+ }
+ imageNode.map["total-vm-size"].value = hex(image.vmSizeToMap());
+ uint64_t sliceOffset = image.sliceOffsetInFile();
+ if ( sliceOffset != 0 )
+ imageNode.map["file-offset-of-slice"].value = hex(sliceOffset);
+ if ( image.hasTextRelocs() )
+ imageNode.map["has-text-relocs"].value = "true";
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ Node segInfoNode;
+ segInfoNode.map["file-offset"].value = hex(fileOffset);
+ segInfoNode.map["file-size"].value = hex(fileSize);
+ segInfoNode.map["vm-size"].value = hex(vmSize);
+ segInfoNode.map["permissions"].value = hex(permissions);
+ imageNode.map["mappings"].array.push_back(segInfoNode);
+ });
+ if ( printFixups ) {
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
+ MemoryRange segContent = { nullptr, vmSize };
+ std::string segName = "segment-" + decimal(segIndex);
+ __block Node segmentFixupsNode;
+ image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
+ switch ( kind ) {
+ case Image::FixupKind::rebase32:
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit rebase";
+ break;
+ case Image::FixupKind::rebase64:
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "64-bit rebase";
+ break;
+ case Image::FixupKind::rebaseText32 :
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit text rebase";
+ break;
+ case Image::FixupKind::bind32:
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit bind, target=") + value.asString(group);
+ break;
+ case Image::FixupKind::bind64:
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("64-bit bind, target=") + value.asString(group);
+ break;
+ case Image::FixupKind::bindText32 :
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text abs bind, target=") + value.asString(group);
+ break;
+ case Image::FixupKind::bindTextRel32 :
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text rel bind, target=") + value.asString(group);
+ break;
+ case Image::FixupKind::bindImportJmp32 :
+ segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit IMPORT JMP rel bind, target=") + value.asString(group);
+ break;
+ }
+ });
+ if ( segmentFixupsNode.map[segName].map.size() != 0 ) {
+ imageNode.map["fixups"].array.push_back(segmentFixupsNode);
+ }
+ });
+ }
+ }
+ else {
+ imageNode.map["patch-start-index"].value = decimal(image.patchStartIndex());
+ imageNode.map["patch-count"].value = decimal(image.patchCount());
+ }
+
+ // add dependents
+ image.forEachDependentImage(groupList, ^(uint32_t depIndex, Image depImage, Image::LinkKind kind, bool& stop) {
+ Node depMapNode;
+ depMapNode.map["path"].value = depImage.path();
+ if ( printDependentsDetails ) {
+ ImageGroup depGroup = depImage.group();
+ uint32_t indexInGroup = depGroup.indexInGroup(depImage.binaryData());
+ depMapNode.map["group-index"].value = decimal(depGroup.groupNum());
+ depMapNode.map["index-in-group"].value = decimal(indexInGroup);
+ }
+ switch ( kind ) {
+ case Image::LinkKind::regular:
+ depMapNode.map["link"].value = "regular";
+ break;
+ case Image::LinkKind::reExport:
+ depMapNode.map["link"].value = "re-export";
+ break;
+ case Image::LinkKind::upward:
+ depMapNode.map["link"].value = "upward";
+ break;
+ case Image::LinkKind::weak:
+ depMapNode.map["link"].value = "weak";
+ break;
+ }
+ imageNode.map["dependents"].array.push_back(depMapNode);
+ });
+ // add things to init before this image
+ __block Node initBeforeNode;
+ image.forEachInitBefore(groupList, ^(Image beforeImage) {
+ Node beforeNode;
+ beforeNode.value = beforeImage.path();
+ imageNode.map["initializer-order"].array.push_back(beforeNode);
+ });
+
+ // add initializers
+ image.forEachInitializer(nullptr, ^(const void* initializer) {
+ Node initNode;
+ initNode.value = hex((long)initializer);
+ imageNode.map["initializer-offsets"].array.push_back(initNode);
+ });
+
+ // add override info if relevant
+ group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) {
+ if ( overrideDylib.binaryData() == image.binaryData() ) {
+ imageNode.map["override-of-cached-dylib"].value = standardDylib.path();
+ }
+ });
+
+ // add dtrace info
+ image.forEachDOF(nullptr, ^(const void* section) {
+ Node initNode;
+ initNode.value = hex((long)section);
+ imageNode.map["dof-offsets"].array.push_back(initNode);
+ });
+
+ return imageNode;
+}
+
+
+static Node buildImageGroupNode(const ImageGroup& group, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+ Node images;
+ uint32_t imageCount = group.imageCount();
+ images.array.reserve(imageCount);
+ for (uint32_t i=0; i < imageCount; ++i) {
+ images.array.push_back(buildImageNode(group.image(i), groupList, printFixups, printDependentsDetails));
+ }
+ return images;
+}
+
+static Node buildClosureNode(const Closure& closure, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+ __block Node root;
+
+ // add env-vars if they exist
+ closure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+ const char* equ = strchr(keyEqualValue, '=');
+ if ( equ != nullptr ) {
+ char key[512];
+ strncpy(key, keyEqualValue, equ-keyEqualValue);
+ key[equ-keyEqualValue] = '\0';
+ root.map["env-vars"].map[key].value = equ+1;
+ }
+ });
+
+ // add missing files array if they exist
+ closure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+ Node fileNode;
+ fileNode.value = path;
+ root.map["must-be-missing-files"].array.push_back(fileNode);
+ });
+
+ const uint8_t* cdHash = closure.cdHash();
+ std::string cdHashStr;
+ cdHashStr.reserve(24);
+ for (int i=0; i < 20; ++i) {
+ uint8_t byte = cdHash[i];
+ uint8_t nibbleL = byte & 0x0F;
+ uint8_t nibbleH = byte >> 4;
+ if ( nibbleH < 10 )
+ cdHashStr += '0' + nibbleH;
+ else
+ cdHashStr += 'a' + (nibbleH-10);
+ if ( nibbleL < 10 )
+ cdHashStr += '0' + nibbleL;
+ else
+ cdHashStr += 'a' + (nibbleL-10);
+ }
+ if ( cdHashStr != "0000000000000000000000000000000000000000" )
+ root.map["cd-hash"].value = cdHashStr;
+
+ // add uuid of dyld cache this closure requires
+ closure.dyldCacheUUID();
+ uuid_string_t cacheUuidStr;
+ uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
+ root.map["dyld-cache-uuid"].value = cacheUuidStr;
+
+ // add top level images
+ Node& rootImages = root.map["root-images"];
+ uint32_t initImageCount = closure.mainExecutableImageIndex();
+ rootImages.array.resize(initImageCount+1);
+ for (uint32_t i=0; i <= initImageCount; ++i) {
+ const Image image = closure.group().image(i);
+ uuid_string_t uuidStr;
+ uuid_unparse(*image.uuid(), uuidStr);
+ rootImages.array[i].value = uuidStr;
+ }
+ root.map["initial-image-count"].value = decimal(closure.initialImageCount());
+
+ // add images
+ root.map["images"] = buildImageGroupNode(closure.group(), groupList, printFixups, printDependentsDetails);
+ root.map["group-num"].value = decimal(closure.group().groupNum());
+
+ if ( closure.mainExecutableUsesCRT() )
+ root.map["main-offset"].value = hex(closure.mainExecutableEntryOffset());
+ else
+ root.map["start-offset"].value = hex(closure.mainExecutableEntryOffset());
+
+ root.map["libdyld-entry-offset"].value = hex(closure.libdyldVectorOffset());
+
+ root.map["restricted"].value = (closure.isRestricted() ? "true" : "false");
+
+ root.map["library-validation"].value = (closure.usesLibraryValidation() ? "true" : "false");
+
+ __block Node cacheOverrides;
+ closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
+ Node patch;
+ patch.map["patch-index"].value = decimal(patchTableIndex);
+ patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
+ cacheOverrides.array.push_back(patch);
+ });
+ if ( !cacheOverrides.array.empty() )
+ root.map["dyld-cache-overrides"].array = cacheOverrides.array;
+
+ return root;
+}
+
+static void indentBy(uint32_t spaces, FILE* out) {
+ for (int i=0; i < spaces; ++i) {
+ fprintf(out, " ");
+ }
+}
+
+static void printJSON(const Node& node, uint32_t indent, FILE* out)
+{
+ if ( !node.map.empty() ) {
+ fprintf(out, "{");
+ bool needComma = false;
+ for (const auto& entry : node.map) {
+ if ( needComma )
+ fprintf(out, ",");
+ fprintf(out, "\n");
+ indentBy(indent+2, out);
+ fprintf(out, "\"%s\": ", entry.first.c_str());
+ printJSON(entry.second, indent+2, out);
+ needComma = true;
+ }
+ fprintf(out, "\n");
+ indentBy(indent, out);
+ fprintf(out, "}");
+ }
+ else if ( !node.array.empty() ) {
+ fprintf(out, "[");
+ bool needComma = false;
+ for (const auto& entry : node.array) {
+ if ( needComma )
+ fprintf(out, ",");
+ fprintf(out, "\n");
+ indentBy(indent+2, out);
+ printJSON(entry, indent+2, out);
+ needComma = true;
+ }
+ fprintf(out, "\n");
+ indentBy(indent, out);
+ fprintf(out, "]");
+ }
+ else {
+ fprintf(out, "\"%s\"", node.value.c_str());
+ }
+ if ( indent == 0 )
+ fprintf(out, "\n");
+}
+
+
+void Image::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+ Node image = buildImageNode(*this, groupList, printFixups, printDependentsDetails);
+ printJSON(image, 0, out);
+}
+
+void ImageGroup::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+ Node root;
+ root.map["images"] = buildImageGroupNode(*this, groupList, printFixups, printDependentsDetails);
+ root.map["group-num"].value = decimal(groupNum());
+ root.map["dylibs-expected-on-disk"].value = (dylibsExpectedOnDisk() ? "true" : "false");
+ printJSON(root, 0, out);
+}
+
+void ImageGroup::printStatistics(FILE* out) const
+{
+ __block uint32_t totalRebases = 0;
+ __block uint32_t totalBinds = 0;
+ for (uint32_t i=0; i < imageCount(); ++i) {
+ Image img(image(i));
+ img.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
+ MemoryRange segContent = { nullptr, vmSize };
+ img.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
+ if ( kind == Image::FixupKind::rebase64 )
+ ++totalRebases;
+ else
+ ++totalBinds;
+ });
+ });
+ }
+
+ fprintf(out, "ImageGroup:\n");
+ fprintf(out, " image-count: % 5d\n", _binaryData->imagesPoolCount);
+ fprintf(out, " alias-count: % 5d\n", _binaryData->imageAliasCount);
+ fprintf(out, " segments-count: % 5d\n", _binaryData->segmentsPoolCount);
+ fprintf(out, " dependents-count: % 5d\n", _binaryData->dependentsPoolCount);
+ fprintf(out, " targets-count: % 5d\n", _binaryData->targetsPoolCount);
+ fprintf(out, " rebase-count: % 5d\n", totalRebases);
+ fprintf(out, " bind-count: % 5d\n", totalBinds);
+ fprintf(out, " fixups-size: % 8d bytes\n", _binaryData->fixupsPoolSize);
+ fprintf(out, " targets-size: % 8ld bytes\n", _binaryData->targetsPoolCount * sizeof(uint64_t));
+ fprintf(out, " strings-size: % 8d bytes\n", _binaryData->stringsPoolSize);
+ fprintf(out, " dofs-size: % 8ld bytes\n", _binaryData->dofOffsetPoolCount * sizeof(uint32_t));
+ fprintf(out, " indirect-groups-size: % 8ld bytes\n", _binaryData->indirectGroupNumPoolCount * sizeof(uint32_t));
+}
+
+
+void Closure::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+ Node root = buildClosureNode(*this, groupList, printFixups, printDependentsDetails);
+ printJSON(root, 0, out);
+}
+
+void Closure::printStatistics(FILE* out) const
+{
+ fprintf(out, "closure size: %lu\n", size());
+ group().printStatistics(out);
+}
+
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+#endif
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCache.h"
+#include "MachOParser.h"
+#include "DyldCacheParser.h"
+
+namespace dyld {
+ extern void log(const char* format, ...) __attribute__((format(printf, 1, 2)));
+}
+
+namespace dyld3 {
+namespace launch_cache {
+
+static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end) {
+ assert("malformed uleb128");
+ break;
+ }
+ uint64_t slice = *p & 0x7f;
+
+ if (bit > 63) {
+ assert("uleb128 too big for uint64");
+ break;
+ }
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ } while (*p++ & 0x80);
+ return (uintptr_t)result;
+}
+
+
+bool MemoryRange::contains(const MemoryRange& other) const
+{
+ if ( this->address > other.address )
+ return false;
+ const uint8_t* thisEnd = (uint8_t*)address + size;
+ const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
+ return (thisEnd >= otherEnd);
+}
+
+bool MemoryRange::intersects(const MemoryRange& other) const
+{
+ const uint8_t* thisEnd = (uint8_t*)address + size;
+ const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
+ if ( otherEnd < this->address )
+ return false;
+ return ( other.address < thisEnd );
+}
+
+
+//////////////////////////// SlowLoadSet ////////////////////////////////////////
+
+bool SlowLoadSet::contains(const BinaryImageData* image)
+{
+ for (const BinaryImageData** p=_start; p < _current; ++p) {
+ if ( *p == image )
+ return true;
+ }
+ return false;
+}
+
+bool SlowLoadSet::add(const BinaryImageData* image)
+{
+ if ( _current < _end ) {
+ *_current++ = image;
+ return true;
+ }
+ return false;
+}
+
+void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*))
+{
+ for (const BinaryImageData** p=_start; p < _current; ++p) {
+ handler(*p);
+ }
+}
+
+void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*, bool& stop))
+{
+ bool stop = false;
+ for (const BinaryImageData** p=_start; p < _current; ++p) {
+ handler(*p, stop);
+ if ( stop )
+ break;
+ }
+}
+
+
+long SlowLoadSet::count() const
+{
+ return (_current - _start);
+}
+
+
+//////////////////////////// TargetSymbolValue ////////////////////////////////////////
+
+
+#if DYLD_IN_PROCESS
+
+uintptr_t TargetSymbolValue::resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const
+{
+ // this block is only used if findExportedSymbol() needs to trace re-exported dylibs to find a symbol
+ MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ *foundMH = nullptr;
+ images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+ Image anImage(binImage);
+ if ( strcmp(depLoadPath, anImage.path()) == 0 ) {
+ *foundMH = mh;
+ stop = true;
+ }
+ });
+ return (*foundMH != nullptr);
+ };
+
+ uintptr_t offset;
+ switch ( _data.sharedCache.kind ) {
+
+ case TargetSymbolValue::kindSharedCache:
+ assert(_data.sharedCache.offsetIntoCache != 0);
+ return (uintptr_t)(images.dyldCacheLoadAddressForImage() + _data.sharedCache.offsetIntoCache);
+
+ case TargetSymbolValue::kindAbsolute:
+ offset = (uintptr_t)_data.absolute.value;
+ // sign extend 42 bit value
+ if ( offset & 0x2000000000000000ULL )
+ offset |= 0xC000000000000000ULL;
+ return offset;
+
+ case TargetSymbolValue::kindGroup: {
+ uint32_t groupNum = _data.group.isIndirectGroup ? inGroup.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
+ uintptr_t targetImageLoadAddress = (uintptr_t)(images.loadAddressFromGroupAndIndex(groupNum, _data.group.indexInGroup));
+ if ( targetImageLoadAddress == 0 )
+ diag.error("image for groupNum=%d, indexInGroup=%d not found", groupNum, _data.group.indexInGroup);
+ offset = (uintptr_t)_data.group.offsetInImage;
+ // sign extend 42 bit offset
+ if ( offset & 0x0000020000000000ULL )
+ offset |= 0xFFFFFC0000000000ULL;
+ return targetImageLoadAddress + offset;
+ }
+
+ case TargetSymbolValue::kindDynamicGroup: {
+ const char* imagePath = inGroup.stringFromPool(_data.dynamicGroup.imagePathOffset);
+ const char* symbolName = inGroup.stringFromPool(_data.dynamicGroup.symbolNameOffset);
+ __block uintptr_t result = 0;
+ __block bool found = false;
+ if ( strcmp(imagePath, "@flat") == 0 ) {
+ // search all images in load order
+ images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+ Diagnostics findSymbolDiag;
+ dyld3::MachOParser parser(mh);
+ dyld3::MachOParser::FoundSymbol foundInfo;
+ if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, ^(uint32_t, const char* depLoadPath, void*, const mach_header** foundMH, void**) {
+ // <rdar://problem/31921090> need to follow re-exported symbols to support libc renamed and reexported symbols
+ *foundMH = nullptr;
+ images.forEachImage(^(uint32_t innerIndex, const BinaryImageData* innerBinImage, const mach_header* innerMH, bool& innerStop) {
+ Image innerImage(innerBinImage);
+ if ( strcmp(depLoadPath, innerImage.path()) == 0 ) {
+ *foundMH = innerMH;
+ innerStop = true;
+ }
+ });
+ return (*foundMH != nullptr);
+ }) ) {
+ result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+ images.setAsNeverUnload(idx);
+ found = true;
+ stop = true;
+ }
+ });
+ // <rdar://problem/31944092> bind unfound flat symbols to NULL to support lazy binding semantics
+ if ( !found ) {
+ result = 0;
+ found = true;
+ }
+ }
+ else if ( strcmp(imagePath, "@main") == 0 ) {
+ // search only main executable
+ images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+ if ( mh->filetype == MH_EXECUTE ) {
+ Diagnostics findSymbolDiag;
+ dyld3::MachOParser parser(mh);
+ dyld3::MachOParser::FoundSymbol foundInfo;
+ if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
+ result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+ found = true;
+ stop = true;
+ }
+ }
+ });
+ }
+ else if ( strcmp(imagePath, "@weak_def") == 0 ) {
+ // search images with weak definitions in load order
+ images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+ Image anImage(binImage);
+ if ( anImage.hasWeakDefs() ) {
+ Diagnostics findSymbolDiag;
+ dyld3::MachOParser parser(mh);
+ dyld3::MachOParser::FoundSymbol foundInfo;
+ if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
+ result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+ found = true;
+ images.setAsNeverUnload(idx);
+ stop = true;
+ }
+ }
+ });
+ }
+ else {
+ // search only image the matches supplied path
+ images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+ Image anImage(binImage);
+ if ( strcmp(anImage.path(), imagePath) == 0 ) {
+ Diagnostics findSymbolDiag;
+ dyld3::MachOParser parser(mh);
+ dyld3::MachOParser::FoundSymbol foundInfo;
+ if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, reExportFollower) ) {
+ result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+ found = true;
+ stop = true;
+ }
+ }
+ });
+ }
+ if ( found )
+ return result;
+ if ( _data.dynamicGroup.weakImport )
+ return 0;
+ diag.error("dynamic symbol '%s' not found for %s", symbolName, imagePath);
+ return 0;
+ }
+ }
+ assert(0 && "resolveTarget() not reachable");
+}
+
+#else
+
+TargetSymbolValue::TargetSymbolValue()
+{
+ _data.raw = 0;
+}
+
+TargetSymbolValue TargetSymbolValue::makeInvalid()
+{
+ return TargetSymbolValue();
+}
+
+TargetSymbolValue TargetSymbolValue::makeSharedCacheOffset(uint32_t offset)
+{
+ TargetSymbolValue t;
+ t._data.sharedCache.kind = kindSharedCache;
+ t._data.sharedCache.offsetIntoCache = offset;
+ return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeAbsolute(uint64_t value)
+{
+ TargetSymbolValue t;
+ t._data.absolute.kind = kindAbsolute;
+ t._data.absolute.value = value;
+ return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum)
+{
+ assert(groupIndex != 0 || isIndirectGroupNum);
+ assert(groupIndex < 128);
+ assert(imageIndexInGroup < 4096);
+ TargetSymbolValue t;
+ t._data.group.kind = kindGroup;
+ t._data.group.isIndirectGroup = isIndirectGroupNum;
+ t._data.group.groupNum = groupIndex;
+ t._data.group.indexInGroup = imageIndexInGroup;
+ t._data.group.offsetInImage = offsetInImage;
+ return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport)
+{
+ TargetSymbolValue t;
+ t._data.dynamicGroup.kind = kindDynamicGroup;
+ t._data.dynamicGroup.weakImport = weakImport;
+ t._data.dynamicGroup.imagePathOffset = imagePathPoolOffset;
+ t._data.dynamicGroup.symbolNameOffset = imageSymbolPoolOffset;
+ return t;
+}
+
+bool TargetSymbolValue::isSharedCacheTarget(uint64_t& offsetInCache) const
+{
+ if ( _data.sharedCache.kind != kindSharedCache )
+ return false;
+ offsetInCache = _data.sharedCache.offsetIntoCache;
+ return true;
+}
+
+bool TargetSymbolValue::isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const
+{
+ if ( _data.sharedCache.kind != kindGroup )
+ return false;
+ // This is only used for interposing, so refuse to allow indirect for group 2
+ assert(!_data.group.isIndirectGroup);
+ groupNum = _data.group.groupNum;
+ indexInGroup = _data.group.indexInGroup;
+ offsetInImage = _data.group.offsetInImage;
+ return true;
+}
+
+bool TargetSymbolValue::isInvalid() const
+{
+ return (_data.raw == 0);
+}
+
+static std::string hex8(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%08llX", value);
+ return buff;
+}
+
+static std::string decimal(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "%llu", value);
+ return buff;
+}
+
+std::string TargetSymbolValue::asString(ImageGroup group) const
+{
+ int64_t offset;
+ switch ( _data.sharedCache.kind ) {
+ case kindSharedCache:
+ if ( _data.sharedCache.offsetIntoCache == 0 )
+ return "{invalid target}";
+ else
+ return "{cache+" + hex8(_data.sharedCache.offsetIntoCache) + "}";
+ case kindAbsolute:
+ offset = (uintptr_t)_data.absolute.value;
+ // sign extend 42 bit value
+ if ( offset & 0x2000000000000000ULL )
+ offset |= 0xC000000000000000ULL;
+ return "{absolute:" + hex8(offset) + "}";
+ case kindGroup:
+ offset = _data.group.offsetInImage;
+ // sign extend 42 bit offset
+ if ( offset & 0x0000020000000000ULL )
+ offset |= 0xFFFFFC0000000000ULL;
+ if ( _data.group.groupNum == 1 )
+ return "{otherDylib[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+ if ( _data.group.groupNum == 2 )
+ return "{closure[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+ else {
+ uint32_t groupNum = _data.group.isIndirectGroup ? group.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
+ return "{dlopen-group-" + decimal(groupNum-2) + "[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+ }
+ case kindDynamicGroup:
+ return "{dynamic image='" + std::string(group.stringFromPool(_data.dynamicGroup.imagePathOffset))
+ + "' symbol='" + std::string(group.stringFromPool(_data.dynamicGroup.symbolNameOffset)) + "'}";
+ }
+ assert(0 && "unreachable");
+ return "xx";
+}
+
+#endif
+
+//////////////////////////// ImageRef ////////////////////////////////////////
+
+binary_format::ImageRef binary_format::ImageRef::weakImportMissing()
+{
+ ImageRef missing(0xFFFFFFFF);
+ return missing;
+}
+
+
+
+//////////////////////////// Closure ////////////////////////////////////////
+
+Closure::Closure(const binary_format::Closure* closure)
+ : _binaryData(closure)
+{
+ assert(closure->magic == binary_format::Closure::magicV1);
+}
+
+size_t Closure::size() const
+{
+ return _binaryData->stringPoolOffset + _binaryData->stringPoolSize;
+}
+
+const ImageGroup Closure::group() const
+{
+ return ImageGroup(&_binaryData->group);
+}
+
+void Closure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
+{
+ const uint32_t* envVarStringOffsets = (uint32_t*)((uint8_t*)_binaryData + _binaryData->dyldEnvVarsOffset);
+ const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset;
+ bool stop = false;
+ for (uint32_t i=0; i < _binaryData->dyldEnvVarsCount; ++i) {
+ handler(&stringPool[envVarStringOffsets[i]], stop);
+ if ( stop )
+ break;
+ }
+}
+
+void Closure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
+{
+ const uint16_t* offsets = (uint16_t*)((uint8_t*)_binaryData + _binaryData->missingFileComponentsOffset);
+ if ( *offsets == 0 )
+ return;
+ const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset;
+ bool stop = false;
+ while ( !stop ) {
+ char path[PATH_MAX];
+ path[0] = '\0';
+ while ( *offsets != 0 ) {
+ const char* component = &stringPool[*offsets++];
+ strlcat(path, "/", PATH_MAX);
+ strlcat(path, component, PATH_MAX);
+ }
+ handler(path, stop);
+ ++offsets; // move to next path
+ if ( *offsets == 0 ) // if no next path, then end of list of strings
+ stop = true;
+ }
+}
+
+const uuid_t* Closure::dyldCacheUUID() const
+{
+ return &(_binaryData->dyldCacheUUID);
+}
+
+
+const uint8_t* Closure::cdHash() const
+{
+ return _binaryData->mainExecutableCdHash;
+}
+
+
+uint32_t Closure::initialImageCount() const
+{
+ return _binaryData->initialImageCount;
+}
+
+
+uint32_t Closure::mainExecutableImageIndex() const
+{
+ return _binaryData->mainExecutableIndexInGroup;
+}
+
+
+uint32_t Closure::mainExecutableEntryOffset() const
+{
+ return _binaryData->mainExecutableEntryOffset;
+}
+
+bool Closure::mainExecutableUsesCRT() const
+{
+ return _binaryData->usesCRT;
+}
+
+bool Closure::isRestricted() const
+{
+ return _binaryData->isRestricted;
+}
+
+bool Closure::usesLibraryValidation() const
+{
+ return _binaryData->usesLibraryValidation;
+}
+
+uint32_t Closure::libdyldVectorOffset() const
+{
+ return _binaryData->libdyldVectorOffset;
+}
+
+const BinaryImageData* Closure::libSystem(const ImageGroupList& groups)
+{
+ return Image::resolveImageRef(groups, _binaryData->libSystemRef).binaryData();
+}
+
+const BinaryImageData* Closure::libDyld(const ImageGroupList& groups)
+{
+ return Image::resolveImageRef(groups, _binaryData->libDyldRef).binaryData();
+}
+
+
+//////////////////////////// ImageGroup ////////////////////////////////////////
+
+size_t ImageGroup::size() const
+{
+ return (_binaryData->stringsPoolOffset + _binaryData->stringsPoolSize + 3) & (-4);
+}
+
+uint32_t ImageGroup::groupNum() const
+{
+ return _binaryData->groupNum;
+}
+
+bool ImageGroup::dylibsExpectedOnDisk() const
+{
+ return _binaryData->dylibsExpectedOnDisk;
+}
+
+uint32_t ImageGroup::imageCount() const
+{
+ return _binaryData->imagesPoolCount;
+}
+
+const binary_format::Image* ImageGroup::imageBinary(uint32_t index) const
+{
+ assert(index <_binaryData->imagesPoolCount);
+ return (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset + (index * _binaryData->imagesEntrySize));
+}
+
+
+const Image ImageGroup::image(uint32_t index) const
+{
+ return Image(imageBinary(index));
+}
+
+uint32_t ImageGroup::indexInGroup(const binary_format::Image* img) const
+{
+ long delta = (char*)img - ((char*)_binaryData + _binaryData->imagesPoolOffset);
+ uint32_t index = (uint32_t)(delta /_binaryData->imagesEntrySize);
+ assert(image(index)._binaryData == img);
+ return index;
+}
+
+const binary_format::Image* ImageGroup::findImageByPath(const char* path, uint32_t& foundIndex) const
+{
+ // check path of each image in group
+ uint32_t targetHash = hashFunction(path);
+ const uint8_t* p = (uint8_t*)_binaryData + _binaryData->imagesPoolOffset;
+ for (uint32_t i=0; i < _binaryData->imagesPoolCount; ++i) {
+ const binary_format::Image* binImage = (binary_format::Image*)p;
+ if ( binImage->pathHash == targetHash ) {
+ Image img(binImage);
+ if ( !img.isInvalid() && (strcmp(img.path(), path) == 0) ) {
+ foundIndex = i;
+ return binImage;
+ }
+ }
+ p += _binaryData->imagesEntrySize;
+ }
+ // check each alias
+ const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
+ for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
+ const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
+ if ( aliasEntries[i].aliasHash == targetHash ) {
+ if ( strcmp(aliasPath, path) == 0 ) {
+ Image img = image(aliasEntries[i].imageIndexInGroup);
+ if ( !img.isInvalid() ) {
+ foundIndex = aliasEntries[i].imageIndexInGroup;
+ return img.binaryData();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+const binary_format::Image* ImageGroup::findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const
+{
+ assert(groupNum() == 0);
+
+ const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)segmentPool(0);
+ const binary_format::Image* image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
+ // most address lookups are in TEXT, so just search first segment in first pass
+ for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
+ const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex];
+ if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
+ mhCacheOffset = segInfo->cacheOffset;
+ foundPermissions = segInfo->permissions;
+ return image;
+ }
+ image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
+ }
+ // second pass, skip TEXT segment
+ image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
+ for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
+ for (uint32_t segIndex=1; segIndex < image->segmentsArrayCount; ++segIndex) {
+ const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex+segIndex];
+ if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
+ mhCacheOffset = cacheSegs[image->segmentsArrayStartIndex].cacheOffset;
+ foundPermissions = segInfo->permissions;
+ return image;
+ }
+ }
+ image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
+ }
+ return nullptr;
+}
+
+void ImageGroup::forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const
+{
+ bool stop = false;
+ const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
+ for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
+ if ( aliasEntries[i].imageIndexInGroup == imageIndex ) {
+ const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
+ handler(aliasPath, aliasEntries[i].aliasHash, stop);
+ if ( stop )
+ break;
+ }
+ }
+}
+
+const char* ImageGroup::stringPool() const
+{
+ return (char*)_binaryData + _binaryData->stringsPoolOffset;
+}
+
+const char* ImageGroup::stringFromPool(uint32_t offset) const
+{
+ assert(offset < _binaryData->stringsPoolSize);
+ return (char*)_binaryData + _binaryData->stringsPoolOffset + offset;
+}
+
+uint32_t ImageGroup::stringPoolSize() const
+{
+ return _binaryData->stringsPoolSize;;
+}
+
+binary_format::ImageRef ImageGroup::dependentPool(uint32_t index) const
+{
+ assert(index < _binaryData->dependentsPoolCount);
+ const binary_format::ImageRef* depArray = (binary_format::ImageRef*)((char*)_binaryData + _binaryData->dependentsPoolOffset);
+ return depArray[index];
+}
+
+const uint64_t* ImageGroup::segmentPool(uint32_t index) const
+{
+ assert(index < _binaryData->segmentsPoolCount);
+ const uint64_t* segArray = (uint64_t*)((char*)_binaryData + _binaryData->segmentsPoolOffset);
+ return &segArray[index];
+}
+
+
+const uint32_t* ImageGroup::initializerOffsetsPool() const
+{
+ return (uint32_t*)((char*)_binaryData + _binaryData->intializerOffsetPoolOffset);
+}
+
+const uint32_t ImageGroup::initializerOffsetsCount() const
+{
+ return _binaryData->intializerOffsetPoolCount;
+}
+
+const binary_format::ImageRef* ImageGroup::intializerListPool() const
+{
+ return (binary_format::ImageRef*)((char*)_binaryData + _binaryData->intializerListPoolOffset);
+}
+
+const uint32_t ImageGroup::intializerListPoolCount() const
+{
+ return _binaryData->intializerListPoolCount;
+}
+
+const binary_format::AllFixupsBySegment* ImageGroup::fixUps(uint32_t offset) const
+{
+ return (binary_format::AllFixupsBySegment*)((char*)_binaryData + _binaryData->fixupsOffset + offset);
+}
+
+const TargetSymbolValue* ImageGroup::targetValuesArray() const
+{
+ return (TargetSymbolValue*)((char*)_binaryData + _binaryData->targetsOffset);
+}
+
+uint32_t ImageGroup::targetValuesCount() const
+{
+ return _binaryData->targetsPoolCount;
+}
+
+
+const uint32_t* ImageGroup::dofOffsetsPool() const
+{
+ return (uint32_t*)((char*)_binaryData + _binaryData->dofOffsetPoolOffset);
+}
+
+const uint32_t ImageGroup::dofOffsetsCount() const
+{
+ return _binaryData->dofOffsetPoolCount;
+}
+
+
+const uint32_t* ImageGroup::indirectGroupNumsPool() const
+{
+ return (uint32_t*)((char*)_binaryData + _binaryData->indirectGroupNumPoolOffset);
+}
+
+const uint32_t ImageGroup::indirectGroupNumsCount() const
+{
+ return _binaryData->indirectGroupNumPoolCount;
+}
+
+uint32_t ImageGroup::indirectGroupNum(uint32_t offset) const
+{
+ assert(offset < _binaryData->indirectGroupNumPoolCount);
+ return indirectGroupNumsPool()[offset];
+}
+
+uint32_t ImageGroup::hashFunction(const char* str)
+{
+ uint32_t h = 0;
+ for (const char* s=str; *s != '\0'; ++s)
+ h = h*5 + *s;
+ return h;
+}
+
+
+void ImageGroup::forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset, void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const
+{
+ assert(_binaryData->imagesEntrySize == sizeof(binary_format::CachedImage) && "only callable on group-0 in shared cache");
+ assert(patchTargetIndex < _binaryData->cachePatchTableCount);
+ const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
+ uint32_t offsetsIndex = patches[patchTargetIndex].offsetsStartIndex;
+ uint32_t targetCacheOffset = patches[patchTargetIndex].targetCacheOffset;
+ const binary_format::PatchOffset* patchLocationOffsets = (binary_format::PatchOffset*)((char*)_binaryData + _binaryData->cachePatchOffsetsOffset);
+ bool stop = false;
+ while ( !stop ) {
+ assert(offsetsIndex < _binaryData->cachePatchOffsetsCount);
+ binary_format::PatchOffset entry = patchLocationOffsets[offsetsIndex];
+ ++offsetsIndex;
+ handler(targetCacheOffset, cacheDataVmOffset+entry.dataRegionOffset, entry.hasAddend, stop);
+ if ( entry.last )
+ stop = true;
+ }
+}
+
+void ImageGroup::forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop)) const
+{
+ bool stop = false;
+ const binary_format::ImageRefOverride* entries = (binary_format::ImageRefOverride*)((char*)_binaryData + _binaryData->imageOverrideTableOffset);
+ for (uint32_t i=0; (i < _binaryData->imageOverrideTableCount) && !stop; ++i) {
+ handler(entries[i].standardDylib, entries[i].overrideDylib, stop);
+ }
+}
+
+void ImageGroup::forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDylib, bool& stop)) const
+{
+ forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop) {
+ Image standardDylib = Image::resolveImageRef(groupList, standardDylibRef, false);
+ Image overrideDylib = Image::resolveImageRef(groupList, overrideDylibRef, false);
+ handler(standardDylib, overrideDylib, stop);
+ });
+}
+
+
+#if DYLD_IN_PROCESS
+
+void ImageGroup::forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex, void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool&)) const
+{
+ DyldCacheParser cacheParser((DyldSharedCache*)dyldCacheLoadAddress, false);
+ uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
+ forEachDyldCachePatch(patchTargetIndex, cacheDataVmOffset, ^(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop) {
+ uintptr_t addend = 0;
+ uintptr_t* fixupLoc = (uintptr_t*)((char*)dyldCacheLoadAddress + usePointersCacheOffset);
+ if ( hasAddend ) {
+ uintptr_t currentValue = *fixupLoc;
+ uintptr_t expectedValue = (uintptr_t)dyldCacheLoadAddress + targetCacheOffset;
+ uintptr_t delta = currentValue - expectedValue;
+ assert(delta < 32);
+ addend = delta;
+ }
+ handler(fixupLoc, addend, stop);
+ });
+}
+
+void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const
+{
+ bool stop = false;
+ const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
+ for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
+ handler(entries[i].patchTableIndex, imageBinary(entries[i].imageIndex), entries[i].imageOffset, stop);
+ }
+}
+
+#else
+
+void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const
+{
+ bool stop = false;
+ const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
+ for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
+ handler(entries[i].patchTableIndex, entries[i].imageIndex, entries[i].imageOffset, stop);
+ }
+}
+
+void ImageGroup::forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector<uint32_t>& usesPointersCacheOffsets, bool& stop)) const
+{
+ uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
+ __block std::vector<uint32_t> pointerCacheOffsets;
+ bool stop = false;
+ for (uint32_t patchIndex=0; patchIndex < _binaryData->cachePatchTableCount; ++patchIndex) {
+ pointerCacheOffsets.clear();
+ __block uint32_t targetCacheOffset = 0;
+ forEachDyldCachePatch(patchIndex, cacheDataVmOffset, ^(uint32_t targetCacheOff, uint32_t usePointersCacheOffset, bool hasAddend, bool&) {
+ targetCacheOffset = targetCacheOff;
+ pointerCacheOffsets.push_back(usePointersCacheOffset);
+ });
+ std::sort(pointerCacheOffsets.begin(), pointerCacheOffsets.end(), [&](uint32_t a, uint32_t b) { return a < b; });
+ handler(targetCacheOffset, pointerCacheOffsets, stop);
+ if ( stop )
+ break;
+ }
+}
+
+bool ImageGroup::hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& foundIndex) const
+{
+ const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
+ for (uint32_t i=0; i < _binaryData->cachePatchTableCount; ++i) {
+ if ( patches[i].targetCacheOffset == targetCacheOffset ) {
+ foundIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
+
+//////////////////////////// Image ////////////////////////////////////////
+
+
+
+const ImageGroup Image::group() const
+{
+ return ImageGroup((binary_format::ImageGroup*)(((char*)_binaryData) + (_binaryData->groupOffset)));
+}
+
+uint32_t Image::maxLoadCount() const
+{
+ return _binaryData->maxLoadCount;
+}
+
+const char* Image::path() const
+{
+ return group().stringFromPool(_binaryData->pathPoolOffset);
+}
+
+uint32_t Image::pathHash() const
+{
+ return _binaryData->pathHash;
+}
+
+const char* Image::leafName() const
+{
+ const char* path = group().stringFromPool(_binaryData->pathPoolOffset);
+ const char* lastSlash = strrchr(path, '/');
+ if ( lastSlash != nullptr )
+ return lastSlash+1;
+ else
+ return path;
+}
+
+const uuid_t* Image::uuid() const
+{
+ return &(_binaryData->uuid);
+}
+
+bool Image::isInvalid() const
+{
+ return (_binaryData == nullptr) || _binaryData->isInvalid;
+}
+
+bool Image::hasObjC() const
+{
+ return _binaryData->hasObjC;
+}
+
+bool Image::isBundle() const
+{
+ return _binaryData->isBundle;
+}
+
+bool Image::hasWeakDefs() const
+{
+ return _binaryData->hasWeakDefs;
+}
+
+bool Image::mayHavePlusLoads() const
+{
+ return _binaryData->mayHavePlusLoads;
+}
+
+bool Image::hasTextRelocs() const
+{
+ return _binaryData->hasTextRelocs;
+}
+
+bool Image::neverUnload() const
+{
+ return _binaryData->neverUnload;
+}
+
+bool Image::cwdMustBeThisDir() const
+{
+ return _binaryData->cwdSameAsThis;
+}
+
+bool Image::isPlatformBinary() const
+{
+ return _binaryData->isPlatformBinary;
+}
+
+bool Image::overridableDylib() const
+{
+ return _binaryData->overridableDylib;
+}
+
+void Image::forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const
+{
+ assert(!_binaryData->isInvalid);
+ binary_format::ImageRef missingRef = binary_format::ImageRef::weakImportMissing();
+ __block bool stop = false;
+ for (uint32_t depIndex=0; (depIndex < _binaryData->dependentsArrayCount) && !stop; ++depIndex) {
+ binary_format::ImageRef ref = group().dependentPool(_binaryData->dependentsArrayStartIndex + depIndex);
+ if ( ref != missingRef ) {
+ Image depImage(resolveImageRef(groups, ref));
+ handler(depIndex, depImage, (LinkKind)ref.kind(), stop);
+ }
+ }
+}
+
+
+#if !DYLD_IN_PROCESS
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set<const BinaryImageData*>& allDependents) const
+{
+ if ( isInvalid() )
+ return false;
+ __block bool result = true;
+ forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
+ if ( allDependents.count(depImage.binaryData()) == 0 ) {
+ allDependents.insert(depImage.binaryData());
+ if ( !depImage.recurseAllDependentImages(groups, allDependents) ) {
+ result = false;
+ stop = true;
+ }
+ }
+ });
+ return result;
+}
+#endif
+
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped,
+ void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
+{
+ __block bool result = true;
+ // breadth first, add all directly dependent images
+ const dyld3::launch_cache::binary_format::Image* needToProcessArray[_binaryData->dependentsArrayCount];
+ memset((void*)needToProcessArray, 0, _binaryData->dependentsArrayCount * sizeof(*needToProcessArray));
+ const dyld3::launch_cache::binary_format::Image** const needToProcess = needToProcessArray;
+ forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
+ const dyld3::launch_cache::binary_format::Image* depImageData = depImage.binaryData();
+ if ( allDependents.contains(depImageData) ) {
+ needToProcess[depIndex] = nullptr;
+ }
+ else {
+ needToProcess[depIndex] = depImageData;
+ if ( !allDependents.add(depImageData) ) {
+ result = false;
+ stop = true;
+ return;
+ }
+ if (handler) {
+ handler(depImageData, stop);
+ if ( stop )
+ stopped = true;
+ }
+ }
+ });
+
+ // recurse on each dependent image
+ for (int i=0; !stopped && (i < _binaryData->dependentsArrayCount); ++i) {
+ if ( const dyld3::launch_cache::binary_format::Image* depImageData = needToProcess[i] ) {
+ Image depImage(depImageData);
+ if ( !depImage.recurseAllDependentImages(groups, allDependents, stopped, handler) ) {
+ return false;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents,
+ void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
+{
+ bool stopped = false;
+ return recurseAllDependentImages(groups, allDependents, stopped, handler);
+}
+
+void Image::forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+ assert(isDiskImage());
+ const uint32_t pageSize = (_binaryData->has16KBpages ? 0x4000 : 0x1000);
+ const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+ const binary_format::DiskSegment* diskSegs = (binary_format::DiskSegment*)rawSegs;
+ uint32_t segIndex = 0;
+ uint32_t fileOffset = 0;
+ int64_t vmOffset = 0;
+ // decrement vmOffset by all segments before TEXT (e.g. PAGEZERO)
+ for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+ const binary_format::DiskSegment* seg = &diskSegs[i];
+ if ( seg->filePageCount != 0 ) {
+ break;
+ }
+ vmOffset -= (uint64_t)seg->vmPageCount * pageSize;
+ }
+ // walk each segment and call handler
+ for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+ const binary_format::DiskSegment* seg = &diskSegs[i];
+ uint64_t vmSize = (uint64_t)seg->vmPageCount * pageSize;
+ uint32_t fileSize = seg->filePageCount * pageSize;
+ if ( !seg->paddingNotSeg ) {
+ bool stop = false;
+ handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop);
+ ++segIndex;
+ if ( stop )
+ break;
+ }
+ vmOffset += vmSize;
+ fileOffset += fileSize;
+ }
+}
+
+void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+ assert(!isDiskImage());
+ const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+ const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
+ bool stop = false;
+ for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+ uint64_t vmOffset = cacheSegs[i].cacheOffset - cacheSegs[0].cacheOffset;
+ uint64_t vmSize = cacheSegs[i].size;
+ uint8_t permissions = cacheSegs[i].permissions;
+ handler(i, vmOffset, vmSize, permissions, stop);
+ if ( stop )
+ break;
+ }
+}
+
+bool Image::segmentHasFixups(uint32_t segIndex) const
+{
+ return (segmentFixups(segIndex) != nullptr);
+}
+
+bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const
+{
+ if ( addr < imageLoadAddress )
+ return false;
+
+ __block bool found = false;
+ uint64_t offsetInImage = (char*)addr - (char*)imageLoadAddress;
+ if ( _binaryData->isDiskImage ) {
+ forEachDiskSegment(^(uint32_t segIterIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
+ if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
+ if ( permissions != nullptr )
+ *permissions = segPerms;
+ found = true;
+ stop = true;
+ }
+ });
+ }
+ else {
+ forEachCacheSegment(^(uint32_t segIterIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
+ if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
+ if ( permissions != nullptr )
+ *permissions = segPerms;
+ found = true;
+ stop = true;
+ }
+ });
+ }
+ return found;
+}
+
+void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
+{
+ const uint32_t initCount = _binaryData->initOffsetsArrayCount;
+ const uint32_t startIndex = _binaryData->initOffsetsArrayStartIndex;
+ const uint32_t* initOffsets = group().initializerOffsetsPool();
+ assert(startIndex + initCount <= group().initializerOffsetsCount());
+ for (uint32_t i=0; i < initCount; ++i) {
+ uint32_t anOffset = initOffsets[startIndex+i];
+ const void* func = (char*)imageLoadAddress + anOffset;
+ handler(func);
+ }
+}
+
+void Image::forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const
+{
+ const uint32_t initCount = _binaryData->initBeforeArrayCount;
+ const uint32_t startIndex = _binaryData->initBeforeArrayStartIndex;
+ const uint32_t endIndex = group().intializerListPoolCount();
+ const binary_format::ImageRef* initRefs = group().intializerListPool();
+ assert(startIndex + initCount <= endIndex);
+ for (uint32_t i=0; i < initCount; ++i) {
+ binary_format::ImageRef ref = initRefs[startIndex+i];
+ handler(ref);
+ }
+}
+
+void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* section)) const
+{
+ const uint32_t dofCount = _binaryData->dofOffsetsArrayCount;
+ const uint32_t startIndex = _binaryData->dofOffsetsArrayStartIndex;
+ const uint32_t* dofOffsets = group().dofOffsetsPool();
+ assert(startIndex + dofCount <= group().dofOffsetsCount());
+ for (uint32_t i=0; i < dofCount; ++i) {
+ uint32_t anOffset = dofOffsets[startIndex+i];
+ const void* section = (char*)imageLoadAddress + anOffset;
+ handler(section);
+ }
+}
+
+Image Image::resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides)
+{
+ // first look if ref image is overridden in closure
+ __block binary_format::ImageRef targetRef = ref;
+ if ( applyOverrides ) {
+ binary_format::ImageRef refToMatch = ref;
+ refToMatch.clearKind();
+ for (int i=0; i < groups.count(); ++i) {
+ ImageGroup aGroup(groups[i]);
+ if ( aGroup.groupNum() >= 2 ) {
+ aGroup.forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool &stop) {
+ if ( refToMatch == standardDylibRef ) {
+ targetRef = overrideDylibRef;
+ stop = true;
+ }
+ });
+ }
+ }
+ }
+ // create Image object from targetRef
+ for (int i=0; i < groups.count(); ++i) {
+ ImageGroup aGroup(groups[i]);
+ if ( aGroup.groupNum() == targetRef.groupNum() ) {
+ return aGroup.image(targetRef.indexInGroup());
+ }
+ }
+ //assert(0 && "invalid ImageRef");
+ return Image(nullptr);
+}
+
+void Image::forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const
+{
+ forEachInitBefore(^(binary_format::ImageRef ref) {
+ handler(resolveImageRef(groups, ref));
+ });
+}
+
+bool Image::validateUsingModTimeAndInode() const
+{
+ return !group().binaryData()->imageFileInfoIsCdHash;
+}
+
+bool Image::validateUsingCdHash() const
+{
+ // don't have cdHash info if union has modtime info in it
+ if ( !group().binaryData()->imageFileInfoIsCdHash )
+ return false;
+
+ // don't have codesign blob in dyld cache
+ if ( !_binaryData->isDiskImage )
+ return false;
+
+ // return true if image is code signed and cdHash16 is non-zero
+ const binary_format::DiskImage* diskImage = asDiskImage();
+ if ( diskImage->codeSignFileOffset == 0 )
+ return false;
+
+ uint8_t zeros[16];
+ bzero(zeros, 16);
+ return (memcmp(cdHash16(), zeros, 16) != 0);
+}
+
+const uint8_t* Image::cdHash16() const
+{
+ return _binaryData->fileInfo.cdHash16.bytes;
+}
+
+uint64_t Image::fileModTime() const
+{
+ return _binaryData->fileInfo.statInfo.mtime;
+}
+
+uint64_t Image::fileINode() const
+{
+ return _binaryData->fileInfo.statInfo.inode;
+}
+
+
+bool Image::isDiskImage() const
+{
+ return _binaryData->isDiskImage;
+}
+
+const binary_format::DiskImage* Image::asDiskImage() const
+{
+ assert(_binaryData->isDiskImage);
+ return (binary_format::DiskImage*)_binaryData;
+}
+
+const binary_format::CachedImage* Image::asCachedImage() const
+{
+ assert(!_binaryData->isDiskImage);
+ return (binary_format::CachedImage*)_binaryData;
+}
+
+uint32_t Image::pageSize() const
+{
+ return (_binaryData->has16KBpages ? 0x4000 : 0x1000);
+}
+
+uint32_t Image::cacheOffset() const
+{
+ assert(!_binaryData->isDiskImage);
+ const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+ const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
+ return cacheSegs[0].cacheOffset;
+}
+
+uint32_t Image::patchStartIndex() const
+{
+ return asCachedImage()->patchStartIndex;
+}
+
+uint32_t Image::patchCount() const
+{
+ return asCachedImage()->patchCount;
+}
+
+uint64_t Image::sliceOffsetInFile() const
+{
+ return asDiskImage()->sliceOffsetIn4K * 4096;
+}
+
+bool Image::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
+{
+ const binary_format::DiskImage* diskImage = asDiskImage();
+ if ( diskImage->codeSignFileOffset != 0 ) {
+ fileOffset = diskImage->codeSignFileOffset;
+ size = diskImage->codeSignFileSize;
+ return true;
+ }
+ return false;
+}
+
+bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+ const binary_format::DiskImage* diskImage = asDiskImage();
+ if ( diskImage->fairPlayTextPageCount != 0 ) {
+ textOffset = diskImage->fairPlayTextStartPage * pageSize();
+ size = diskImage->fairPlayTextPageCount * pageSize();
+ return true;
+ }
+ return false;
+}
+
+uint64_t Image::vmSizeToMap() const
+{
+ return asDiskImage()->totalVmPages * pageSize();
+}
+
+void Image::forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal,
+ void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t ordinal, bool& stop))
+{
+ bool stop = false;
+ for (const uint8_t* p = pageFixups; (*p != 0) && !stop;) {
+ binary_format::FixUpOpcode fullOp = (binary_format::FixUpOpcode)(*p);
+ binary_format::FixUpOpcode majorOp = (binary_format::FixUpOpcode)(*p & 0xF0);
+ uint8_t low4 = (*p & 0x0F);
+ switch ( majorOp ) {
+ case binary_format::FixUpOpcode::done:
+ return;
+ case binary_format::FixUpOpcode::rebase32: // apply
+ switch ( fullOp ) {
+ case binary_format::FixUpOpcode::bind64:
+ handler(offset, FixupKind::bind64, ordinal, stop);
+ offset += 8;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::bind32:
+ handler(offset, FixupKind::bind32, ordinal, stop);
+ offset += 4;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::rebase64:
+ handler(offset, FixupKind::rebase64, 0, stop);
+ offset += 8;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::rebase32:
+ handler(offset, FixupKind::rebase32, 0, stop);
+ offset += 4;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::rebaseText32:
+ handler(offset, FixupKind::rebaseText32, 0, stop);
+ offset += 4;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::bindText32:
+ handler(offset, FixupKind::bindText32, ordinal, stop);
+ offset += 4;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::bindTextRel32:
+ handler(offset, FixupKind::bindTextRel32, ordinal, stop);
+ offset += 4;
+ ++p;
+ break;
+ case binary_format::FixUpOpcode::bindImportJmp32:
+ handler(offset, FixupKind::bindImportJmp32, ordinal, stop);
+ offset += 5;
+ ++p;
+ break;
+ //case binary_format::FixUpOpcode::fixupChain64:
+ // assert(0 && "rebase/bind chain support not implemented yet");
+ // break;
+ default:
+ assert(0 && "bad opcode");
+ break;
+ }
+ break;
+ case binary_format::FixUpOpcode::incPageOffset:
+ if ( low4 == 0 ) {
+ ++p;
+ offset += read_uleb128(p, p+8)*4;
+ }
+ else {
+ offset += (low4*4);
+ ++p;
+ }
+ break;
+ case binary_format::FixUpOpcode::setPageOffset:
+ if ( low4 == 0 ) {
+ ++p;
+ offset = (uint32_t)read_uleb128(p, p+8);
+ }
+ else {
+ offset = low4;
+ ++p;
+ }
+ break;
+ case binary_format::FixUpOpcode::incOrdinal:
+ if ( low4 == 0 ) {
+ ++p;
+ ordinal += read_uleb128(p, p+8);
+ }
+ else {
+ ordinal += low4;
+ ++p;
+ }
+ break;
+ case binary_format::FixUpOpcode::setOrdinal:
+ if ( low4 == 0 ) {
+ ++p;
+ ordinal = (uint32_t)read_uleb128(p, p+8);
+ }
+ else {
+ ordinal = low4;
+ ++p;
+ }
+ break;
+ case binary_format::FixUpOpcode::repeat: {
+ ++p;
+ uint32_t count = (uint32_t)read_uleb128(p, p+8);
+ uint8_t pattern[32];
+ for (int j=0; j < low4; ++j) {
+ pattern[j] = *p++;
+ }
+ pattern[low4] = (uint8_t)binary_format::FixUpOpcode::done;
+ for (int j=0; j < count; ++j) {
+ forEachFixup(&pattern[0], segContent, offset, ordinal, handler);
+ if ( stop )
+ break;
+ }
+ }
+ break;
+ default:
+ assert(0 && "bad opcode");
+ break;
+ }
+ }
+}
+
+const binary_format::SegmentFixupsByPage* Image::segmentFixups(uint32_t segIndex) const
+{
+ const binary_format::DiskImage* diskImage = asDiskImage();
+ //const BinaryImageGroupData* g = group().binaryData();
+ uint32_t segCountWithFixups = diskImage->fixupsPoolSegCount;
+ //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d), group=%p, segCountWithFixup=%d\n", _binaryData, segIndex, g, segCountWithFixups);
+ const binary_format::AllFixupsBySegment* allFixups = group().fixUps(diskImage->fixupsPoolOffset);
+ for (uint32_t i=0; i < segCountWithFixups; ++i) {
+ if ( allFixups[i].segIndex == segIndex ) {
+ //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) allFixups=%p, allFixups[%d].segIndex=%d, allFixups[%d].offset=%d\n", _binaryData, segIndex, allFixups, i, allFixups[i].segIndex, i, allFixups[i].offset);
+ return (binary_format::SegmentFixupsByPage*)((char*)allFixups + allFixups[i].offset);
+ }
+ }
+ //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) => nullptr\n", _binaryData, segIndex);
+ return nullptr;
+}
+
+void Image::forEachFixup(uint32_t segIndex, MemoryRange segContent, void (^handler)(uint64_t segOffset, FixupKind, TargetSymbolValue, bool& stop)) const
+{
+ const binary_format::SegmentFixupsByPage* segFixups = segmentFixups(segIndex);
+ if ( segFixups == nullptr )
+ return;
+
+ assert(segFixups->pageCount*segFixups->pageSize <= segContent.size);
+
+ const uint32_t ordinalsIndexInGroupPool = asDiskImage()->targetsArrayStartIndex;
+ const uint32_t maxOrdinal = asDiskImage()->targetsArrayCount;
+ const TargetSymbolValue* groupArray = group().targetValuesArray();
+ assert(ordinalsIndexInGroupPool < group().targetValuesCount());
+ const TargetSymbolValue* targetOrdinalArray = &groupArray[ordinalsIndexInGroupPool];
+
+ for (uint32_t pageIndex=0; pageIndex < segFixups->pageCount; ++pageIndex) {
+ const uint8_t* opcodes = (uint8_t*)(segFixups) + segFixups->pageInfoOffsets[pageIndex];
+ uint64_t pageStartOffet = pageIndex * segFixups->pageSize;
+ uint32_t curOffset = 0;
+ uint32_t curOrdinal = 0;
+ forEachFixup(opcodes, segContent.address, curOffset, curOrdinal, ^(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop) {
+ assert(targetOrdinal < maxOrdinal);
+ handler(pageStartOffet + pageOffset, kind, targetOrdinalArray[targetOrdinal], stop);
+ });
+ }
+}
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <dirent.h>
+
+#include <string>
+#include <map>
+#include <list>
+#include <unordered_set>
+#include <unordered_map>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCacheWriter.h"
+#include "shared-cache/dyld_cache_format.h"
+#include "shared-cache/DyldSharedCache.h"
+#include "shared-cache/FileUtils.h"
+
+namespace std
+{
+ template <>
+ struct hash<dyld3::launch_cache::binary_format::ImageRef>
+ {
+ std::size_t operator()(const dyld3::launch_cache::binary_format::ImageRef& value) const {
+ return std::hash<uint16_t>()(value.value());
+ }
+ };
+}
+
+
+namespace dyld3 {
+namespace launch_cache {
+
+
+static uintptr_t align(uintptr_t value, uintptr_t align)
+{
+ return (value+align-1) & (-align);
+}
+
+//////////////////////////// ImageGroupWriter ////////////////////////////////////////
+
+ImageGroupWriter::ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid)
+ : _isDiskImage(groupNum != 0), _is64(is64), _groupNum(groupNum), _pageSize(pages16KB ? 0x4000 : 0x1000),
+ _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _imageFileInfoIsCdHash(!mtimeAndInodeAreValid)
+{
+}
+
+
+uint32_t ImageGroupWriter::size() const
+{
+ binary_format::ImageGroup tempGroup;
+ layoutBinary(&tempGroup);
+ return tempGroup.stringsPoolOffset + tempGroup.stringsPoolSize;
+}
+
+void ImageGroupWriter::layoutBinary(binary_format::ImageGroup* grp) const
+{
+ grp->imagesEntrySize = _isDiskImage ? sizeof(binary_format::DiskImage) : sizeof(binary_format::CachedImage);
+ grp->groupNum = _groupNum;
+ grp->dylibsExpectedOnDisk = _dylibsExpectedOnDisk;
+ grp->imageFileInfoIsCdHash = _imageFileInfoIsCdHash;
+ grp->padding = 0;
+
+ grp->imagesPoolCount = imageCount();
+ grp->imagesPoolOffset = sizeof(binary_format::ImageGroup);
+ uint32_t imagesPoolSize = grp->imagesEntrySize * grp->imagesPoolCount;
+
+ grp->imageAliasCount = (uint32_t)_aliases.size();
+ grp->imageAliasOffset = grp->imagesPoolOffset + imagesPoolSize;
+ uint32_t imageAliasSize = grp->imageAliasCount * sizeof(binary_format::AliasEntry);
+
+ grp->segmentsPoolCount = (uint32_t)_segmentPool.size();
+ grp->segmentsPoolOffset = (uint32_t)align(grp->imageAliasOffset + imageAliasSize, 8);
+ uint32_t segmentsPoolSize = grp->segmentsPoolCount * sizeof(uint64_t);
+
+ grp->dependentsPoolCount = (uint32_t)_dependentsPool.size();
+ grp->dependentsPoolOffset = grp->segmentsPoolOffset + segmentsPoolSize;
+ uint32_t dependentsPoolSize = grp->dependentsPoolCount * sizeof(binary_format::ImageRef);
+
+ grp->intializerOffsetPoolCount = (uint32_t)_initializerOffsets.size();
+ grp->intializerOffsetPoolOffset = (uint32_t)align(grp->dependentsPoolOffset + dependentsPoolSize, 4);
+ uint32_t intializerOffsetSize = grp->intializerOffsetPoolCount * sizeof(uint32_t);
+
+ grp->intializerListPoolCount = (uint32_t)_initializerBeforeLists.size();
+ grp->intializerListPoolOffset = grp->intializerOffsetPoolOffset + intializerOffsetSize;
+ uint32_t intializerListPoolSize = grp->intializerListPoolCount * sizeof(binary_format::ImageRef);
+
+ grp->targetsPoolCount = (uint32_t)_targetsPool.size();
+ grp->targetsOffset = (uint32_t)align(grp->intializerListPoolOffset + intializerListPoolSize, 8);
+ uint32_t targetsSize = grp->targetsPoolCount * sizeof(TargetSymbolValue);
+
+ grp->fixupsPoolSize = (uint32_t)_fixupsPool.size();
+ grp->fixupsOffset = (uint32_t)align(grp->targetsOffset + targetsSize, 4);
+
+ grp->cachePatchTableCount = (uint32_t)_patchPool.size();
+ grp->cachePatchTableOffset = (uint32_t)align(grp->fixupsOffset + grp->fixupsPoolSize, 4);
+ uint32_t patchTableSize = grp->cachePatchTableCount * sizeof(binary_format::PatchTable);
+
+ grp->cachePatchOffsetsCount = (uint32_t)_patchLocationPool.size();
+ grp->cachePatchOffsetsOffset = grp->cachePatchTableOffset + patchTableSize;
+ uint32_t patchOffsetsSize = grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset);
+
+ grp->symbolOverrideTableCount = (uint32_t)_dyldCacheSymbolOverridePool.size();
+ grp->symbolOverrideTableOffset = grp->cachePatchOffsetsOffset + patchOffsetsSize;
+ uint32_t symbolOverrideSize = grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride);
+
+ grp->imageOverrideTableCount = (uint32_t)_imageOverridePool.size();
+ grp->imageOverrideTableOffset = grp->symbolOverrideTableOffset + symbolOverrideSize;
+ uint32_t imageOverrideSize = grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride);
+
+ grp->dofOffsetPoolCount = (uint32_t)_dofOffsets.size();
+ grp->dofOffsetPoolOffset = grp->imageOverrideTableOffset + imageOverrideSize;
+ uint32_t dofOffsetSize = grp->dofOffsetPoolCount * sizeof(uint32_t);
+
+ grp->indirectGroupNumPoolCount = (uint32_t)_indirectGroupNumPool.size();
+ grp->indirectGroupNumPoolOffset = grp->dofOffsetPoolOffset + dofOffsetSize;
+ uint32_t indirectGroupNumSize = grp->indirectGroupNumPoolCount * sizeof(uint32_t);
+
+ grp->stringsPoolSize = (uint32_t)_stringPool.size();
+ grp->stringsPoolOffset = grp->indirectGroupNumPoolOffset + indirectGroupNumSize;
+}
+
+
+void ImageGroupWriter::finalizeTo(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>& curGroups, binary_format::ImageGroup* grp) const
+{
+ layoutBinary(grp);
+ uint8_t* buffer = (uint8_t*)grp;
+ if ( imageCount() > 0 ) {
+ uint32_t pad1Size = grp->segmentsPoolOffset - (grp->imageAliasOffset + grp->imageAliasCount * sizeof(binary_format::AliasEntry));
+ uint32_t pad2Size = grp->targetsOffset - (grp->intializerListPoolOffset + grp->intializerListPoolCount * sizeof(binary_format::ImageRef));
+ memcpy(&buffer[grp->imagesPoolOffset], &imageByIndex(0), grp->imagesEntrySize * grp->imagesPoolCount);
+ memcpy(&buffer[grp->imageAliasOffset], &_aliases[0], grp->imageAliasCount * sizeof(binary_format::AliasEntry));
+ bzero( &buffer[grp->segmentsPoolOffset-pad1Size], pad1Size);
+ memcpy(&buffer[grp->segmentsPoolOffset], &_segmentPool[0], grp->segmentsPoolCount * sizeof(uint64_t));
+ memcpy(&buffer[grp->dependentsPoolOffset], &_dependentsPool[0], grp->dependentsPoolCount * sizeof(binary_format::ImageRef));
+ memcpy(&buffer[grp->intializerListPoolOffset], &_initializerBeforeLists[0], grp->intializerListPoolCount * sizeof(binary_format::ImageRef));
+ memcpy(&buffer[grp->intializerOffsetPoolOffset],&_initializerOffsets[0], grp->intializerOffsetPoolCount * sizeof(uint32_t));
+ bzero( &buffer[grp->targetsOffset-pad2Size], pad2Size);
+ memcpy(&buffer[grp->targetsOffset], &_targetsPool[0], grp->targetsPoolCount * sizeof(TargetSymbolValue));
+ memcpy(&buffer[grp->fixupsOffset], _fixupsPool.start(), grp->fixupsPoolSize);
+ memcpy(&buffer[grp->cachePatchTableOffset], &_patchPool[0], grp->cachePatchTableCount * sizeof(binary_format::PatchTable));
+ memcpy(&buffer[grp->cachePatchOffsetsOffset], &_patchLocationPool[0], grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset));
+ memcpy(&buffer[grp->symbolOverrideTableOffset], &_dyldCacheSymbolOverridePool[0], grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride));
+ memcpy(&buffer[grp->imageOverrideTableOffset], &_imageOverridePool[0], grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride));
+ memcpy(&buffer[grp->dofOffsetPoolOffset], &_dofOffsets[0], grp->dofOffsetPoolCount * sizeof(uint32_t));
+ memcpy(&buffer[grp->indirectGroupNumPoolOffset], &_indirectGroupNumPool[0], grp->indirectGroupNumPoolCount * sizeof(uint32_t));
+ memcpy(&buffer[grp->stringsPoolOffset], &_stringPool[0], grp->stringsPoolSize);
+ }
+
+ // now that we have a real ImageGroup, we can analyze it to find max load counts for each image
+ ImageGroup imGroup(grp);
+ std::unordered_set<const BinaryImageData*> allDependents;
+ STACK_ALLOC_DYNARRAY(const binary_format::ImageGroup*, curGroups.size()+1, newGroupList);
+ for (int i=0; i < curGroups.size(); ++i)
+ newGroupList[i] = curGroups[i];
+ newGroupList[newGroupList.count()-1] = grp;
+ for (uint32_t i=0; i < grp->imagesPoolCount; ++i) {
+ Image image = imGroup.image(i);
+ if ( image.isInvalid() )
+ continue;
+ allDependents.clear();
+ allDependents.insert(image.binaryData());
+ BinaryImageData* imageData = (BinaryImageData*)(buffer + grp->imagesPoolOffset + (i * grp->imagesEntrySize));
+ if ( !image.recurseAllDependentImages(newGroupList, allDependents) ) {
+ //diag.warning("%s dependents on an invalid dylib", image.path());
+ imageData->isInvalid = true;
+ }
+ imageData->maxLoadCount = (uint32_t)allDependents.size();
+ }
+}
+
+uint32_t ImageGroupWriter::maxLoadCount(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>& curGroups, binary_format::ImageGroup* grp) const
+{
+ ImageGroup imGroup(grp);
+ std::unordered_set<const BinaryImageData*> allDependents;
+ std::vector<const BinaryImageGroupData*> allGroups = curGroups;
+ if ( grp->groupNum == 2 )
+ allGroups.push_back(grp);
+ DynArray<const binary_format::ImageGroup*> groupList(allGroups);
+ for (uint32_t i=0; i < grp->imagesPoolCount; ++i) {
+ Image image = imGroup.image(i);
+ if ( image.isInvalid() )
+ continue;
+ allDependents.insert(image.binaryData());
+ BinaryImageData* imageData = (BinaryImageData*)((char*)grp + grp->imagesPoolOffset + (i * grp->imagesEntrySize));
+ if ( !image.recurseAllDependentImages(groupList, allDependents) ) {
+ //diag.warning("%s dependents on an invalid dylib", image.path());
+ imageData->isInvalid = true;
+ }
+ }
+ return (uint32_t)allDependents.size();
+}
+
+void ImageGroupWriter::setImageCount(uint32_t count)
+{
+ if ( _isDiskImage ) {
+ _diskImages.resize(count);
+ bzero(&_diskImages[0], count*sizeof(binary_format::DiskImage));
+ }
+ else {
+ _images.resize(count);
+ bzero(&_images[0], count*sizeof(binary_format::CachedImage));
+ }
+
+ int32_t offset = 0 - (int32_t)sizeof(binary_format::ImageGroup);
+ for (uint32_t i=0; i < count; ++i) {
+ binary_format::Image& img = imageByIndex(i);
+ img.isDiskImage = _isDiskImage;
+ img.has16KBpages = (_pageSize == 0x4000);
+ img.groupOffset = offset;
+ if ( _isDiskImage )
+ offset -= sizeof(binary_format::DiskImage);
+ else
+ offset -= sizeof(binary_format::CachedImage);
+ }
+}
+
+uint32_t ImageGroupWriter::imageCount() const
+{
+ if ( _isDiskImage )
+ return (uint32_t)_diskImages.size();
+ else
+ return (uint32_t)_images.size();
+}
+
+binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex)
+{
+ assert(imageIndex < imageCount());
+ if ( _isDiskImage )
+ return _diskImages[imageIndex];
+ else
+ return _images[imageIndex];
+}
+
+const binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex) const
+{
+ assert(imageIndex < imageCount());
+ if ( _isDiskImage )
+ return _diskImages[imageIndex];
+ else
+ return _images[imageIndex];
+}
+
+bool ImageGroupWriter::isInvalid(uint32_t imageIndex) const
+{
+ return imageByIndex(imageIndex).isInvalid;
+}
+
+void ImageGroupWriter::setImageInvalid(uint32_t imageIndex)
+{
+ imageByIndex(imageIndex).isInvalid = true;
+}
+
+uint32_t ImageGroupWriter::addIndirectGroupNum(uint32_t groupNum)
+{
+ auto pos = _indirectGroupNumPoolExisting.find(groupNum);
+ if ( pos != _indirectGroupNumPoolExisting.end() )
+ return pos->second;
+ uint32_t startOffset = (uint32_t)_indirectGroupNumPool.size();
+ _indirectGroupNumPool.push_back(groupNum);
+ _indirectGroupNumPoolExisting[startOffset] = groupNum;
+ return startOffset;
+}
+
+uint32_t ImageGroupWriter::addString(const char* str)
+{
+ auto pos = _stringPoolExisting.find(str);
+ if ( pos != _stringPoolExisting.end() )
+ return pos->second;
+ uint32_t startOffset = (uint32_t)_stringPool.size();
+ size_t size = strlen(str) + 1;
+ _stringPool.insert(_stringPool.end(), str, &str[size]);
+ _stringPoolExisting[str] = startOffset;
+ return startOffset;
+}
+
+void ImageGroupWriter::alignStringPool()
+{
+ while ( (_stringPool.size() % 4) != 0 )
+ _stringPool.push_back('\0');
+}
+
+void ImageGroupWriter::setImagePath(uint32_t imageIndex, const char* path)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.pathPoolOffset = addString(path);
+ image.pathHash = ImageGroup::hashFunction(path);
+}
+
+void ImageGroupWriter::addImageAliasPath(uint32_t imageIndex, const char* anAlias)
+{
+ binary_format::AliasEntry entry;
+ entry.aliasHash = ImageGroup::hashFunction(anAlias);
+ entry.imageIndexInGroup = imageIndex;
+ entry.aliasOffsetInStringPool = addString(anAlias);
+ _aliases.push_back(entry);
+}
+
+void ImageGroupWriter::ImageGroupWriter::setImageUUID(uint32_t imageIndex, const uuid_t uuid)
+{
+ memcpy(imageByIndex(imageIndex).uuid, uuid, sizeof(uuid_t));
+}
+
+void ImageGroupWriter::setImageHasObjC(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).hasObjC = value;
+}
+
+void ImageGroupWriter::setImageIsBundle(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).isBundle = value;
+}
+
+void ImageGroupWriter::setImageHasWeakDefs(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).hasWeakDefs = value;
+}
+
+void ImageGroupWriter::setImageMayHavePlusLoads(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).mayHavePlusLoads = value;
+}
+
+void ImageGroupWriter::setImageNeverUnload(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).neverUnload = value;
+}
+
+void ImageGroupWriter::setImageMustBeThisDir(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).cwdSameAsThis = value;
+}
+
+void ImageGroupWriter::setImageIsPlatformBinary(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).isPlatformBinary = value;
+}
+
+void ImageGroupWriter::setImageOverridableDylib(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).overridableDylib = value;
+}
+
+void ImageGroupWriter::setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode)
+{
+ imageByIndex(imageIndex).fileInfo.statInfo.mtime = mTime;
+ imageByIndex(imageIndex).fileInfo.statInfo.inode = inode;
+ assert(!_imageFileInfoIsCdHash);
+}
+
+void ImageGroupWriter::setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20])
+{
+ memcpy(imageByIndex(imageIndex).fileInfo.cdHash16.bytes, cdHash, 16);
+ assert(_imageFileInfoIsCdHash);
+}
+
+void ImageGroupWriter::setImageIsEncrypted(uint32_t imageIndex, bool value)
+{
+ imageByIndex(imageIndex).isEncrypted = value;
+}
+
+void ImageGroupWriter::setImageMaxLoadCount(uint32_t imageIndex, uint32_t count)
+{
+ imageByIndex(imageIndex).maxLoadCount = count;
+}
+
+void ImageGroupWriter::setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size)
+{
+ assert(imageIndex < imageCount());
+ assert(_isDiskImage);
+ binary_format::DiskImage& image = _diskImages[imageIndex];
+ if ( image.has16KBpages ) {
+ assert((offset & 0x3FFF) == 0);
+ assert((size & 0x3FFF) == 0);
+ }
+ else {
+ assert((offset & 0xFFF) == 0);
+ assert((size & 0xFFF) == 0);
+ }
+ assert(offset < (_pageSize*16));
+ image.fairPlayTextStartPage = offset / _pageSize;
+ image.fairPlayTextPageCount = size / _pageSize;
+}
+
+void ImageGroupWriter::setImageInitializerOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.initOffsetsArrayStartIndex = _initializerOffsets.size();
+ image.initOffsetsArrayCount = offsets.size();
+ _initializerOffsets.insert(_initializerOffsets.end(), offsets.begin(), offsets.end());
+}
+
+void ImageGroupWriter::setImageDOFOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.dofOffsetsArrayStartIndex = _dofOffsets.size();
+ image.dofOffsetsArrayCount = offsets.size();
+ _dofOffsets.insert(_dofOffsets.end(), offsets.begin(), offsets.end());
+}
+
+uint32_t ImageGroupWriter::addUniqueInitList(const std::vector<binary_format::ImageRef>& initBefore)
+{
+ // see if this initBefore list already exists in pool
+ if ( _initializerBeforeLists.size() > initBefore.size() ) {
+ size_t cmpLen = initBefore.size()*sizeof(binary_format::ImageRef);
+ size_t end = _initializerBeforeLists.size() - initBefore.size();
+ for (uint32_t i=0; i < end; ++i) {
+ if ( memcmp(&initBefore[0], &_initializerBeforeLists[i], cmpLen) == 0 ) {
+ return i;
+ }
+ }
+ }
+ uint32_t result = (uint32_t)_initializerBeforeLists.size();
+ _initializerBeforeLists.insert(_initializerBeforeLists.end(), initBefore.begin(), initBefore.end());
+ return result;
+}
+
+void ImageGroupWriter::setImageInitBefore(uint32_t imageIndex, const std::vector<binary_format::ImageRef>& initBefore)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.initBeforeArrayStartIndex = addUniqueInitList(initBefore);
+ image.initBeforeArrayCount = initBefore.size();
+}
+
+void ImageGroupWriter::setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset)
+{
+ assert(imageIndex < imageCount());
+ assert(_isDiskImage);
+ binary_format::DiskImage& image = _diskImages[imageIndex];
+ image.sliceOffsetIn4K = (uint32_t)(fileOffset / 4096);
+}
+
+void ImageGroupWriter::setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size)
+{
+ assert(imageIndex < imageCount());
+ assert(_isDiskImage);
+ binary_format::DiskImage& image = _diskImages[imageIndex];
+ image.codeSignFileOffset = fileOffset;
+ image.codeSignFileSize = size;
+}
+
+void ImageGroupWriter::setImageDependentsCount(uint32_t imageIndex, uint32_t count)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.dependentsArrayStartIndex = _dependentsPool.size();
+ image.dependentsArrayCount = count;
+ _dependentsPool.resize(_dependentsPool.size() + count);
+}
+
+void ImageGroupWriter::setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent)
+{
+ binary_format::Image& image = imageByIndex(imageIndex);
+ assert(depIndex < image.dependentsArrayCount);
+ _dependentsPool[image.dependentsArrayStartIndex + depIndex] = dependent;
+}
+
+uint32_t ImageGroupWriter::imageDependentsCount(uint32_t imageIndex) const
+{
+ return imageByIndex(imageIndex).dependentsArrayCount;
+}
+
+binary_format::ImageRef ImageGroupWriter::imageDependent(uint32_t imageIndex, uint32_t depIndex) const
+{
+ const binary_format::Image& image = imageByIndex(imageIndex);
+ assert(depIndex < image.dependentsArrayCount);
+ return _dependentsPool[image.dependentsArrayStartIndex + depIndex];
+}
+
+void ImageGroupWriter::setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress)
+{
+ if ( _isDiskImage ) {
+ __block uint32_t totalPageCount = 0;
+ __block uint32_t lastFileOffsetEnd = 0;
+ __block uint64_t lastVmAddrEnd = 0;
+ __block std::vector<binary_format::DiskSegment> diskSegments;
+ diskSegments.reserve(8);
+ imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( (fileOffset != 0) && (fileOffset != lastFileOffsetEnd) ) {
+ binary_format::DiskSegment filePadding;
+ filePadding.filePageCount = (fileOffset - lastFileOffsetEnd)/_pageSize;
+ filePadding.vmPageCount = 0;
+ filePadding.permissions = 0;
+ filePadding.paddingNotSeg = 1;
+ diskSegments.push_back(filePadding);
+ }
+ if ( (lastVmAddrEnd != 0) && (vmAddr != lastVmAddrEnd) ) {
+ binary_format::DiskSegment vmPadding;
+ vmPadding.filePageCount = 0;
+ vmPadding.vmPageCount = (vmAddr - lastVmAddrEnd)/_pageSize;
+ vmPadding.permissions = 0;
+ vmPadding.paddingNotSeg = 1;
+ diskSegments.push_back(vmPadding);
+ totalPageCount += vmPadding.vmPageCount;
+ }
+ {
+ binary_format::DiskSegment segInfo;
+ segInfo.filePageCount = (fileSize+_pageSize-1)/_pageSize;
+ segInfo.vmPageCount = (vmSize+_pageSize-1)/_pageSize;
+ segInfo.permissions = protections & 7;
+ segInfo.paddingNotSeg = 0;
+ diskSegments.push_back(segInfo);
+ totalPageCount += segInfo.vmPageCount;
+ if ( fileSize != 0 )
+ lastFileOffsetEnd = fileOffset + fileSize;
+ if ( vmSize != 0 )
+ lastVmAddrEnd = vmAddr + vmSize;
+ }
+ });
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.segmentsArrayStartIndex = _segmentPool.size();
+ image.segmentsArrayCount = diskSegments.size();
+ _segmentPool.insert(_segmentPool.end(), (uint64_t*)&diskSegments[0], (uint64_t*)&diskSegments[image.segmentsArrayCount]);
+ _diskImages[imageIndex].totalVmPages = totalPageCount;
+ }
+ else {
+ binary_format::Image& image = imageByIndex(imageIndex);
+ image.segmentsArrayStartIndex = _segmentPool.size();
+ image.segmentsArrayCount = imageParser.segmentCount();
+ _segmentPool.resize(_segmentPool.size() + image.segmentsArrayCount);
+ __block uint32_t segIndex = 0;
+ imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ binary_format::DyldCacheSegment seg = { (uint32_t)(vmAddr-cacheUnslideBaseAddress), (uint32_t)vmSize, protections };
+ _segmentPool[image.segmentsArrayStartIndex + segIndex] = *((uint64_t*)&seg);
+ ++segIndex;
+ });
+ }
+}
+
+void ImageGroupWriter::setImagePatchLocations(uint32_t imageIndex, uint32_t funcVmOffset, const std::unordered_set<uint32_t>& patchLocations)
+{
+ assert(imageIndex < imageCount());
+ binary_format::CachedImage& image = _images[imageIndex];
+ if ( image.patchStartIndex == 0 ) {
+ image.patchStartIndex = (uint32_t)_patchPool.size();
+ image.patchCount = 0;
+ }
+ else {
+ assert(image.patchStartIndex + image.patchCount == _patchPool.size());
+ }
+
+ binary_format::PatchTable entry = { funcVmOffset, (uint32_t)_patchLocationPool.size() };
+ for (uint32_t loc : patchLocations) {
+ _patchLocationPool.push_back(*((binary_format::PatchOffset*)&loc));
+ }
+ _patchLocationPool.back().last = true;
+ _patchPool.push_back(entry);
+ _images[imageIndex].patchCount++;
+}
+
+void ImageGroupWriter::setGroupCacheOverrides(const std::vector<binary_format::DyldCacheOverride>& cacheOverrides)
+{
+ _dyldCacheSymbolOverridePool = cacheOverrides;
+}
+
+void ImageGroupWriter::addImageIsOverride(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef)
+{
+ _imageOverridePool.push_back({standardDylibRef, overrideDylibRef});
+}
+
+
+class SegmentFixUpBuilder
+{
+public:
+ SegmentFixUpBuilder(uint32_t segIndex, uint32_t dataSegPageCount, uint32_t pageSize, bool is64,
+ const std::vector<ImageGroupWriter::FixUp>& fixups,
+ std::vector<TargetSymbolValue>& targetsForImage, bool log);
+
+ bool hasFixups() { return _hasFixups; }
+ uint32_t segIndex() { return _segIndex; }
+ void appendSegmentFixUpMap(ContentBuffer&);
+
+private:
+ struct TmpOpcode {
+ binary_format::FixUpOpcode op;
+ uint8_t repeatOpcodeCount;
+ uint16_t count;
+
+ bool operator!=(const TmpOpcode& rhs) const {
+ return ((op != rhs.op) || (count != rhs.count) || (repeatOpcodeCount != rhs.repeatOpcodeCount));
+ }
+ };
+
+
+ ContentBuffer makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start,
+ const ImageGroupWriter::FixUp* end);
+ uint32_t getOrdinalForTarget(TargetSymbolValue);
+ void expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[0x4000], uint32_t& offset, uint32_t& ordinal);
+ void expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[0x4000]);
+ bool samePageContent(const uint8_t page1[], const uint8_t page2[]);
+ void printOpcodes(const char* prefix, const std::vector<TmpOpcode> opcodes);
+ void printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset);
+ uint32_t opcodeEncodingSize(const std::vector<TmpOpcode>& opcodes);
+
+ const bool _is64;
+ const bool _log;
+ bool _hasFixups;
+ const uint32_t _segIndex;
+ const uint32_t _dataSegPageCount;
+ const uint32_t _pageSize;
+ std::vector<TargetSymbolValue>& _targets;
+ std::vector<ContentBuffer> _opcodesByPage;
+};
+
+
+
+
+SegmentFixUpBuilder::SegmentFixUpBuilder(uint32_t segIndex, uint32_t segPageCount, uint32_t pageSize, bool is64,
+ const std::vector<ImageGroupWriter::FixUp>& fixups,
+ std::vector<TargetSymbolValue>& targetsForImage, bool log)
+ : _is64(is64), _log(log), _hasFixups(false), _segIndex(segIndex), _dataSegPageCount(segPageCount), _pageSize(pageSize), _targets(targetsForImage)
+{
+ //fprintf(stderr, "SegmentFixUpBuilder(segIndex=%d, segPageCount=%d)\n", segIndex, segPageCount);
+ _targets.push_back(TargetSymbolValue::makeInvalid()); // ordinal zero reserved to mean "add slide"
+ _opcodesByPage.resize(segPageCount);
+ size_t startFixupIndex = 0;
+ for (uint32_t pageIndex=0; pageIndex < segPageCount; ++pageIndex) {
+ uint32_t pageStartOffset = pageIndex*_pageSize;
+ uint32_t pageEndOffset = pageStartOffset+_pageSize;
+ // find first index in this page
+ while ( (startFixupIndex < fixups.size()) && ((fixups[startFixupIndex].segIndex != segIndex) || (fixups[startFixupIndex].segOffset < pageStartOffset)) )
+ ++startFixupIndex;
+ // find first index beyond this page
+ size_t endFixupIndex = startFixupIndex;
+ while ( (endFixupIndex < fixups.size()) && (fixups[endFixupIndex].segIndex == segIndex) && (fixups[endFixupIndex].segOffset < pageEndOffset) )
+ ++endFixupIndex;
+ // create opcodes for fixups on this pageb
+ _opcodesByPage[pageIndex] = makeFixupOpcodesForPage(pageStartOffset, &fixups[startFixupIndex], &fixups[endFixupIndex]);
+ startFixupIndex = endFixupIndex;
+ }
+}
+
+
+uint32_t SegmentFixUpBuilder::getOrdinalForTarget(TargetSymbolValue target)
+{
+ uint32_t ordinal = 0;
+ for (const TargetSymbolValue& entry : _targets) {
+ if ( entry == target )
+ return ordinal;
+ ++ordinal;
+ }
+ _targets.push_back(target);
+ return ordinal;
+}
+
+void SegmentFixUpBuilder::appendSegmentFixUpMap(ContentBuffer& buffer)
+{
+ std::vector<uint32_t> offsets;
+ uint32_t curOffset = sizeof(binary_format::SegmentFixupsByPage)-4 + _dataSegPageCount*4;
+ for (auto& opcodes : _opcodesByPage) {
+ if ( opcodes.size() == 0 )
+ offsets.push_back(0);
+ else
+ offsets.push_back(curOffset);
+ curOffset += opcodes.size();
+ }
+ uint32_t totalSize = curOffset;
+
+ // write header
+ buffer.append_uint32(totalSize); // SegmentFixupsByPage.size
+ buffer.append_uint32(_pageSize); // SegmentFixupsByPage.pageSize
+ buffer.append_uint32(_dataSegPageCount); // SegmentFixupsByPage.pageCount
+ for (uint32_t i=0; i < _dataSegPageCount; ++i) {
+ buffer.append_uint32(offsets[i]); // SegmentFixupsByPage.pageInfoOffsets[i]
+ }
+ // write each page's opcode stream
+ for (uint32_t i=0; i < offsets.size(); ++i) {
+ buffer.append_buffer(_opcodesByPage[i]);
+ }
+}
+
+void SegmentFixUpBuilder::expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[])
+{
+ uint32_t offset = 0;
+ uint32_t ordinal = 0;
+ bzero(page, _pageSize);
+ expandOpcodes(opcodes, page, offset, ordinal);
+}
+
+void SegmentFixUpBuilder::expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[], uint32_t& offset, uint32_t& ordinal)
+{
+ for (int i=0; i < opcodes.size(); ++i) {
+ assert(offset < _pageSize);
+ TmpOpcode tmp = opcodes[i];
+ switch ( tmp.op ) {
+ case binary_format::FixUpOpcode::bind64:
+ *(uint64_t*)(&page[offset]) = ordinal;
+ offset += 8;
+ break;
+ case binary_format::FixUpOpcode::bind32:
+ *(uint32_t*)(&page[offset]) = ordinal;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::rebase64:
+ *(uint64_t*)(&page[offset]) = 0x1122334455667788;
+ offset += 8;
+ break;
+ case binary_format::FixUpOpcode::rebase32:
+ *(uint32_t*)(&page[offset]) = 0x23452345;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::rebaseText32:
+ *(uint32_t*)(&page[offset]) = 0x56785678;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindText32:
+ *(uint32_t*)(&page[offset]) = 0x98769876;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindTextRel32:
+ *(uint32_t*)(&page[offset]) = 0x34563456;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindImportJmp32:
+ *(uint32_t*)(&page[offset]) = 0x44556677;
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::done:
+ break;
+ case binary_format::FixUpOpcode::setPageOffset:
+ offset = tmp.count;
+ break;
+ case binary_format::FixUpOpcode::incPageOffset:
+ offset += (tmp.count*4);
+ break;
+ case binary_format::FixUpOpcode::setOrdinal:
+ ordinal = tmp.count;
+ break;
+ case binary_format::FixUpOpcode::incOrdinal:
+ ++ordinal;
+ break;
+ case binary_format::FixUpOpcode::repeat: {
+ std::vector<TmpOpcode> pattern;
+ for (int j=0; j < tmp.repeatOpcodeCount; ++j) {
+ pattern.push_back(opcodes[i+j+1]);
+ }
+ for (int j=0; j < tmp.count; ++j) {
+ expandOpcodes(pattern, page, offset, ordinal);
+ }
+ i += tmp.repeatOpcodeCount;
+ }
+ break;
+ }
+ }
+}
+
+
+
+uint32_t SegmentFixUpBuilder::opcodeEncodingSize(const std::vector<TmpOpcode>& opcodes)
+{
+ uint32_t size = 0;
+ for (int i=0; i < opcodes.size(); ++i) {
+ switch ( opcodes[i].op ) {
+ case binary_format::FixUpOpcode::bind64:
+ case binary_format::FixUpOpcode::bind32:
+ case binary_format::FixUpOpcode::rebase64:
+ case binary_format::FixUpOpcode::rebase32:
+ case binary_format::FixUpOpcode::rebaseText32:
+ case binary_format::FixUpOpcode::bindText32:
+ case binary_format::FixUpOpcode::bindTextRel32:
+ case binary_format::FixUpOpcode::bindImportJmp32:
+ case binary_format::FixUpOpcode::done:
+ ++size;
+ break;
+ case binary_format::FixUpOpcode::setPageOffset:
+ case binary_format::FixUpOpcode::incPageOffset:
+ case binary_format::FixUpOpcode::setOrdinal:
+ case binary_format::FixUpOpcode::incOrdinal:
+ ++size;
+ if ( opcodes[i].count >= 16 )
+ size += ContentBuffer::uleb128_size(opcodes[i].count);
+ break;
+ case binary_format::FixUpOpcode::repeat: {
+ ++size;
+ size += ContentBuffer::uleb128_size(opcodes[i].count);
+ std::vector<TmpOpcode> pattern;
+ for (int j=0; j < opcodes[i].repeatOpcodeCount; ++j) {
+ pattern.push_back(opcodes[++i]);
+ }
+ size += opcodeEncodingSize(pattern);
+ }
+ break;
+ }
+ }
+ return size;
+}
+
+
+bool SegmentFixUpBuilder::samePageContent(const uint8_t page1[], const uint8_t page2[])
+{
+ bool result = true;
+ if (memcmp(page1, page2, _pageSize) != 0) {
+ if ( _is64 ) {
+ const uint64_t* p1 = (uint64_t* )page1;
+ const uint64_t* p2 = (uint64_t* )page2;
+ for (int i=0; i < _pageSize/8; ++i) {
+ if ( p1[i] != p2[i] ) {
+ fprintf(stderr, "page1[0x%03X] = 0x%016llX, page2[0x%03X] = 0x%016llX\n", i*8, p1[i], i*8, p2[i]);
+ result = false;
+ }
+ }
+ }
+ else {
+ const uint32_t* p1 = (uint32_t* )page1;
+ const uint32_t* p2 = (uint32_t* )page2;
+ for (int i=0; i < _pageSize/4; ++i) {
+ if ( p1[i] != p2[i] ) {
+ fprintf(stderr, "page1[0x%03X] = 0x%016X, page2[0x%03X] = 0x%016X\n", i*4, p1[i], i*4, p2[i]);
+ result = false;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+void SegmentFixUpBuilder::printOpcodes(const char* prefix, const std::vector<TmpOpcode> opcodes)
+{
+ uint32_t offset = 0;
+ printOpcodes(prefix, true, &opcodes[0], opcodes.size(), offset);
+}
+
+void SegmentFixUpBuilder::printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset)
+{
+ for (int i=0; i < opcodesLen; ++i) {
+ TmpOpcode tmp = opcodes[i];
+ if ( printOffset )
+ fprintf(stderr, "%s offset=0x%04X: ", prefix, offset);
+ else
+ fprintf(stderr, "%s ", prefix);
+ switch ( tmp.op ) {
+ case binary_format::FixUpOpcode::bind64:
+ fprintf(stderr, "bind64\n");
+ offset += 8;
+ break;
+ case binary_format::FixUpOpcode::bind32:
+ fprintf(stderr, "bind32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::rebase64:
+ fprintf(stderr, "rebase64\n");
+ offset += 8;
+ break;
+ case binary_format::FixUpOpcode::rebase32:
+ fprintf(stderr, "rebase32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::rebaseText32:
+ fprintf(stderr, "rebaseText32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindText32:
+ fprintf(stderr, "bindText32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindTextRel32:
+ fprintf(stderr, "bindTextRel32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::bindImportJmp32:
+ fprintf(stderr, "bindJmpRel32\n");
+ offset += 4;
+ break;
+ case binary_format::FixUpOpcode::done:
+ fprintf(stderr, "done\n");
+ break;
+ case binary_format::FixUpOpcode::setPageOffset:
+ fprintf(stderr, "setPageOffset(%d)\n", tmp.count);
+ offset = tmp.count;
+ break;
+ case binary_format::FixUpOpcode::incPageOffset:
+ fprintf(stderr, "incPageOffset(%d)\n", tmp.count);
+ offset += (tmp.count*4);
+ break;
+ case binary_format::FixUpOpcode::setOrdinal:
+ fprintf(stderr, "setOrdinal(%d)\n", tmp.count);
+ break;
+ case binary_format::FixUpOpcode::incOrdinal:
+ fprintf(stderr, "incOrdinal(%d)\n", tmp.count);
+ break;
+ case binary_format::FixUpOpcode::repeat: {
+ char morePrefix[128];
+ strcpy(morePrefix, prefix);
+ strcat(morePrefix, " ");
+ uint32_t prevOffset = offset;
+ fprintf(stderr, "repeat(%d times, next %d opcodes)\n", tmp.count, tmp.repeatOpcodeCount);
+ printOpcodes(morePrefix, false, &opcodes[i+1], tmp.repeatOpcodeCount, offset);
+ i += tmp.repeatOpcodeCount;
+ uint32_t repeatDelta = (offset-prevOffset)*(tmp.count-1);
+ offset += repeatDelta;
+ }
+ break;
+ }
+ }
+}
+
+ContentBuffer SegmentFixUpBuilder::makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start, const ImageGroupWriter::FixUp* end)
+{
+ //fprintf(stderr, " makeFixupOpcodesForPage(segOffset=0x%06X, startFixup=%p, endFixup=%p)\n", pageStartSegmentOffset, start, end);
+ std::vector<TmpOpcode> tmpOpcodes;
+ const uint32_t pointerSize = (_is64 ? 8 : 4);
+ uint32_t offset = pageStartSegmentOffset;
+ uint32_t ordinal = 0;
+ const ImageGroupWriter::FixUp* lastFixup = nullptr;
+ for (const ImageGroupWriter::FixUp* f=start; f < end; ++f) {
+ // ignore double bind at same address (ld64 bug)
+ if ( lastFixup && (lastFixup->segOffset == f->segOffset) )
+ continue;
+ // add opcode to adjust current offset if needed
+ if ( f->segOffset != offset ) {
+ if ( ((f->segOffset % 4) != 0) || ((offset % 4) != 0) ) {
+ // mis aligned pointers use bigger set opcode
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::setPageOffset, 0, (uint16_t)(f->segOffset-pageStartSegmentOffset)});
+ }
+ else {
+ uint32_t delta4 = (uint32_t)(f->segOffset - offset)/4;
+ assert(delta4*4 < _pageSize);
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::incPageOffset, 0, (uint16_t)delta4});
+ }
+ offset = (uint32_t)f->segOffset;
+ }
+ uint32_t nextOrd = 0;
+ switch ( f->type ) {
+ case ImageGroupWriter::FixupType::rebase:
+ tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::rebase64 : binary_format::FixUpOpcode::rebase32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::pointerLazyBind:
+ case ImageGroupWriter::FixupType::pointerBind:
+ //assert(f->target.imageIndex == binary_format::OrdinalEntry::kImageIndexDyldSharedCache);
+ nextOrd = getOrdinalForTarget(f->target);
+ if ( nextOrd != ordinal ) {
+ if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+ }
+ else {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+ }
+ ordinal = nextOrd;
+ }
+ tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::bind64 : binary_format::FixUpOpcode::bind32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::rebaseText:
+ assert(!_is64);
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::rebaseText32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::bindText:
+ assert(!_is64);
+ nextOrd = getOrdinalForTarget(f->target);
+ if ( nextOrd != ordinal ) {
+ if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+ }
+ else {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+ }
+ ordinal = nextOrd;
+ }
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::bindText32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::bindTextRel:
+ assert(!_is64);
+ nextOrd = getOrdinalForTarget(f->target);
+ if ( nextOrd != ordinal ) {
+ if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+ }
+ else {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+ }
+ ordinal = nextOrd;
+ }
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::bindTextRel32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::bindImportJmpRel:
+ assert(!_is64);
+ nextOrd = getOrdinalForTarget(f->target);
+ if ( nextOrd != ordinal ) {
+ if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+ }
+ else {
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+ }
+ ordinal = nextOrd;
+ }
+ tmpOpcodes.push_back({binary_format::FixUpOpcode::bindImportJmp32, 0, 0});
+ offset += pointerSize;
+ _hasFixups = true;
+ break;
+ case ImageGroupWriter::FixupType::ignore:
+ assert(0 && "ignore fixup types should have been removed");
+ break;
+ }
+ lastFixup = f;
+ }
+
+ uint8_t firstExpansion[0x4010]; // larger than 16KB to handle unaligned pointers
+ expandOpcodes(tmpOpcodes, firstExpansion);
+
+ if (_log) printOpcodes("start", tmpOpcodes);
+
+
+ for (int stride=1; stride < 6; ++stride) {
+ for (int i=0; i < tmpOpcodes.size(); ++i) {
+ int j;
+ for (j=i+stride; j < tmpOpcodes.size(); j += stride) {
+ bool strideMatch = true;
+ for (int k=0; k < stride; ++k) {
+ if ( (j+k >= tmpOpcodes.size()) || (tmpOpcodes[j+k] != tmpOpcodes[i+k]) ) {
+ strideMatch = false;
+ break;
+ }
+ if ( (tmpOpcodes[j+k].op == binary_format::FixUpOpcode::repeat) && (tmpOpcodes[j+k].repeatOpcodeCount+k >= stride) ) {
+ strideMatch = false;
+ break;
+ }
+ }
+ if ( !strideMatch )
+ break;
+ }
+ // see if same opcode repeated three or more times
+ int repeats = (j-i)/stride;
+ if ( repeats > 3 ) {
+ // replace run with repeat opcode
+ tmpOpcodes[i].op = binary_format::FixUpOpcode::repeat;
+ tmpOpcodes[i].repeatOpcodeCount = stride;
+ tmpOpcodes[i].count = repeats;
+ tmpOpcodes.erase(tmpOpcodes.begin()+i+1, tmpOpcodes.begin()+j-stride);
+ i += stride;
+ }
+ else {
+ // don't look for matches inside a repeat loop
+ if ( tmpOpcodes[i].op == binary_format::FixUpOpcode::repeat )
+ i += tmpOpcodes[i].repeatOpcodeCount;
+ }
+ }
+ if (_log) {
+ char tmp[32];
+ sprintf(tmp, "stride %d", stride);
+ printOpcodes(tmp, tmpOpcodes);
+ }
+ uint8_t secondExpansion[0x4010];
+ expandOpcodes(tmpOpcodes, secondExpansion);
+ if ( !samePageContent(firstExpansion, secondExpansion) )
+ printOpcodes("opt", tmpOpcodes);
+ }
+
+ // convert temp opcodes to real opcodes
+ bool wroteDone = false;
+ ContentBuffer opcodes;
+ for (const TmpOpcode& tmp : tmpOpcodes) {
+ switch ( tmp.op ) {
+ case binary_format::FixUpOpcode::bind64:
+ case binary_format::FixUpOpcode::bind32:
+ case binary_format::FixUpOpcode::rebase64:
+ case binary_format::FixUpOpcode::rebase32:
+ case binary_format::FixUpOpcode::rebaseText32:
+ case binary_format::FixUpOpcode::bindText32:
+ case binary_format::FixUpOpcode::bindTextRel32:
+ case binary_format::FixUpOpcode::bindImportJmp32:
+ opcodes.append_byte((uint8_t)tmp.op);
+ break;
+ case binary_format::FixUpOpcode::done:
+ opcodes.append_byte((uint8_t)tmp.op);
+ wroteDone = true;
+ break;
+ case binary_format::FixUpOpcode::setPageOffset:
+ case binary_format::FixUpOpcode::incPageOffset:
+ case binary_format::FixUpOpcode::setOrdinal:
+ case binary_format::FixUpOpcode::incOrdinal:
+ if ( (tmp.count > 0) && (tmp.count < 16) ) {
+ opcodes.append_byte((uint8_t)tmp.op | tmp.count);
+ }
+ else {
+ opcodes.append_byte((uint8_t)tmp.op);
+ opcodes.append_uleb128(tmp.count);
+ }
+ break;
+ case binary_format::FixUpOpcode::repeat: {
+ const TmpOpcode* nextOpcodes = &tmp;
+ ++nextOpcodes;
+ std::vector<TmpOpcode> pattern;
+ for (int i=0; i < tmp.repeatOpcodeCount; ++i) {
+ pattern.push_back(nextOpcodes[i]);
+ }
+ uint32_t repeatBytes = opcodeEncodingSize(pattern);
+ assert(repeatBytes < 15);
+ opcodes.append_byte((uint8_t)tmp.op | repeatBytes);
+ opcodes.append_uleb128(tmp.count);
+ }
+ break;
+ }
+ }
+
+ if ( (opcodes.size() == 0) || !wroteDone )
+ opcodes.append_byte((uint8_t)binary_format::FixUpOpcode::done);
+
+ // make opcodes streams 4-byte aligned
+ opcodes.pad_to_size(4);
+
+ //fprintf(stderr, " makeFixupOpcodesForPage(pageStartSegmentOffset=0x%0X) result=%lu bytes\n", pageStartSegmentOffset, opcodes.size());
+
+ return opcodes;
+}
+
+
+
+
+void ImageGroupWriter::setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector<FixUp>& fixups, bool hasTextRelocs)
+{
+ // only applicable for ImageGroup in a closure (not group of images in dyld cache)
+ assert(_isDiskImage);
+
+ // sort all rebases and binds by address
+ std::sort(fixups.begin(), fixups.end(), [](FixUp& lhs, FixUp& rhs) -> bool {
+ if ( &lhs == &rhs )
+ return false;
+ // sort by segIndex
+ if ( lhs.segIndex < rhs.segIndex )
+ return true;
+ if ( lhs.segIndex > rhs.segIndex )
+ return false;
+ // then sort by segOffset
+ if ( lhs.segOffset < rhs.segOffset )
+ return true;
+ if ( lhs.segOffset > rhs.segOffset )
+ return false;
+ // two fixups at same location
+
+ // if the same (linker bug), ignore one
+ if ( lhs.type == rhs.type ) {
+ rhs.type = FixupType::ignore;
+ }
+ // if one is rebase for lazy pointer, ignore rebase because dyld3 does not lazy bind
+ else if ( (lhs.type == FixupType::pointerLazyBind) && (rhs.type == FixupType::rebase) ) {
+ // lazy pointers have rebase and (lazy) bind at same location. since dyld3 does not do lazy binding, we mark the rebase to be ignored later
+ rhs.type = FixupType::ignore;
+ }
+ else if ( (rhs.type == FixupType::pointerLazyBind) && (lhs.type == FixupType::rebase) ) {
+ // lazy pointers have rebase and (lazy) bind at same location. since dyld3 does not do lazy binding, we mark the rebase to be ignored later
+ lhs.type = FixupType::ignore;
+ }
+ return (lhs.type < rhs.type);
+ });
+
+ // remove ignoreable fixups
+ fixups.erase(std::remove_if(fixups.begin(), fixups.end(),
+ [&](const FixUp& a) {
+ return (a.type == FixupType::ignore);
+ }), fixups.end());
+
+ // look for overlapping fixups
+ const uint32_t pointerSize = (_is64 ? 8 : 4);
+ const FixUp* lastFixup = nullptr;
+ for (const FixUp& fixup : fixups) {
+ if ( lastFixup != nullptr ) {
+ if ( lastFixup->segIndex == fixup.segIndex ) {
+ uint64_t increment = fixup.segOffset - lastFixup->segOffset;
+ if ( increment < pointerSize ) {
+ if ( (increment == 0) && ((lastFixup->type == FixupType::ignore) || (fixup.type == FixupType::ignore)) ) {
+ // allow rebase to local lazy helper and lazy bind to same location
+ }
+ else {
+ diag.error("segment %d has overlapping fixups at offset 0x%0llX and 0x%0llX", fixup.segIndex, lastFixup->segOffset, fixup.segOffset);
+ setImageInvalid(imageIndex);
+ return;
+ }
+ }
+ }
+ }
+ lastFixup = &fixup;
+ }
+
+ if ( hasTextRelocs )
+ _diskImages[imageIndex].hasTextRelocs = true;
+
+ // there is one ordinal table per image, shared by all segments with fixups in that image
+ std::vector<TargetSymbolValue> targetsForImage;
+
+ const bool opcodeLogging = false;
+ // calculate SegmentFixupsByPage for each segment
+ std::vector<SegmentFixUpBuilder*> builders;
+ for (uint32_t segIndex=0, onDiskSegIndex=0; segIndex < _diskImages[imageIndex].segmentsArrayCount; ++segIndex) {
+ const binary_format::DiskSegment* diskSeg = (const binary_format::DiskSegment*)&(_segmentPool[_diskImages[imageIndex].segmentsArrayStartIndex+segIndex]);
+ SegmentFixUpBuilder* builder = nullptr;
+ if ( diskSeg->paddingNotSeg )
+ continue;
+ if ( diskSeg->filePageCount == 0 ) {
+ ++onDiskSegIndex;
+ continue;
+ }
+ if ( diskSeg->permissions & VM_PROT_WRITE ) {
+ builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging);
+ }
+ else if ( hasTextRelocs && (diskSeg->permissions == (VM_PROT_READ|VM_PROT_EXECUTE)) ) {
+ builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging);
+ }
+ if ( builder != nullptr ) {
+ if ( builder->hasFixups() )
+ builders.push_back(builder);
+ else
+ delete builder;
+ }
+ ++onDiskSegIndex;
+ }
+
+ // build AllFixupsBySegment for image
+ _fixupsPool.pad_to_size(4);
+ uint32_t startOfFixupsOffset = (uint32_t)_fixupsPool.size();
+ size_t headerSize = builders.size() * sizeof(binary_format::AllFixupsBySegment);
+ size_t offsetOfSegmentHeaderInBuffer = _fixupsPool.size();
+ for (int i=0; i < headerSize; ++i) {
+ _fixupsPool.append_byte(0);
+ }
+ uint32_t entryIndex = 0;
+ for (SegmentFixUpBuilder* builder : builders) {
+ binary_format::AllFixupsBySegment* entries = (binary_format::AllFixupsBySegment*)(_fixupsPool.start()+offsetOfSegmentHeaderInBuffer);
+ entries[entryIndex].segIndex = builder->segIndex();
+ entries[entryIndex].offset = (uint32_t)_fixupsPool.size() - startOfFixupsOffset;
+ builder->appendSegmentFixUpMap(_fixupsPool);
+ delete builder;
+ ++entryIndex;
+ }
+ _diskImages[imageIndex].fixupsPoolOffset = (uint32_t)offsetOfSegmentHeaderInBuffer;
+ _diskImages[imageIndex].fixupsPoolSegCount = entryIndex;
+
+ // append targetsForImage into group
+ size_t start = _targetsPool.size();
+ size_t count = targetsForImage.size();
+ _diskImages[imageIndex].targetsArrayStartIndex = (uint32_t)start;
+ _diskImages[imageIndex].targetsArrayCount = (uint32_t)count;
+ assert(_diskImages[imageIndex].targetsArrayStartIndex == start);
+ assert(_diskImages[imageIndex].targetsArrayCount == count);
+ _targetsPool.insert(_targetsPool.end(), targetsForImage.begin(), targetsForImage.end());
+}
+
+
+}
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 LaunchCacheWriter_h
+#define LaunchCacheWriter_h
+
+
+#include <stdint.h>
+
+#include <vector>
+#include <list>
+#include <unordered_map>
+#include <map>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCache.h"
+#include "MachOParser.h"
+#include "shared-cache/DyldSharedCache.h"
+
+
+namespace dyld3 {
+namespace launch_cache {
+
+
+
+class ContentBuffer {
+private:
+ std::vector<uint8_t> _data;
+public:
+ std::vector<uint8_t>& bytes() { return _data; }
+ unsigned long size() const { return _data.size(); }
+ void reserve(unsigned long l) { _data.reserve(l); }
+ const uint8_t* start() const { return &_data[0]; }
+ const uint8_t* end() const { return &_data[_data.size()]; }
+
+ void append_uleb128(uint64_t value) {
+ uint8_t byte;
+ do {
+ byte = value & 0x7F;
+ value &= ~0x7F;
+ if ( value != 0 )
+ byte |= 0x80;
+ _data.push_back(byte);
+ value = value >> 7;
+ } while( byte >= 0x80 );
+ }
+
+ void append_byte(uint8_t byte) {
+ _data.push_back(byte);
+ }
+
+ void append_uint32(uint32_t value) {
+ for (int i=0; i < 4; ++i) {
+ _data.push_back(value & 0xFF);
+ value = (value >> 8);
+ }
+ }
+
+ void append_uint64(uint64_t value) {
+ for (int i=0; i < 8; ++i) {
+ _data.push_back(value & 0xFF);
+ value = (value >> 8);
+ }
+ }
+
+ void append_buffer(const ContentBuffer& value) {
+ _data.insert(_data.end(), value.start(), value.end());
+ }
+
+ static unsigned int uleb128_size(uint64_t value) {
+ uint32_t result = 0;
+ do {
+ value = value >> 7;
+ ++result;
+ } while ( value != 0 );
+ return result;
+ }
+
+ void pad_to_size(unsigned int alignment) {
+ while ( (_data.size() % alignment) != 0 )
+ _data.push_back(0);
+ }
+};
+
+class ImageGroupWriter
+{
+public:
+ ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid);
+
+ enum class FixupType { rebase, pointerBind, pointerLazyBind, bindText, bindTextRel, rebaseText, bindImportJmpRel, ignore };
+ struct FixUp {
+ uint32_t segIndex;
+ uint64_t segOffset;
+ FixupType type;
+ TargetSymbolValue target;
+ };
+
+ uint32_t size() const;
+ void finalizeTo(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>&, binary_format::ImageGroup* buffer) const;
+ uint32_t maxLoadCount(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>&, binary_format::ImageGroup* buffer) const;
+
+ bool isInvalid(uint32_t imageIndex) const;
+
+ void setImageCount(uint32_t);
+ void setImageInvalid(uint32_t imageIndex);
+ void setImagePath(uint32_t imageIndex, const char* path);
+ void setImageUUID(uint32_t imageIndex, const uuid_t uuid);
+ void setImageHasObjC(uint32_t imageIndex, bool value);
+ void setImageIsBundle(uint32_t imageIndex, bool value);
+ void setImageHasWeakDefs(uint32_t imageIndex, bool value);
+ void setImageMayHavePlusLoads(uint32_t imageIndex, bool value);
+ void setImageNeverUnload(uint32_t imageIndex, bool);
+ void setImageMustBeThisDir(uint32_t imageIndex, bool value);
+ void setImageIsPlatformBinary(uint32_t imageIndex, bool value);
+ void setImageOverridableDylib(uint32_t imageIndex, bool value);
+ void setImageIsEncrypted(uint32_t imageIndex, bool value);
+ void setImageMaxLoadCount(uint32_t imageIndex, uint32_t count);
+ void setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size);
+ void setImageInitializerOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets);
+ void setImageDOFOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets);
+ void setImageInitBefore(uint32_t imageIndex, const std::vector<binary_format::ImageRef>&);
+ void setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset);
+ void setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode);
+ void setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20]);
+ void setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size);
+ void setImageDependentsCount(uint32_t imageIndex, uint32_t count);
+ void setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent);
+ void setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress);
+ void setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector<FixUp>& fixups, bool hasTextRelocs);
+ void addImageAliasPath(uint32_t imageIndex, const char* anAlias);
+ void setImagePatchLocations(uint32_t imageIndex, uint32_t funcOffset, const std::unordered_set<uint32_t>& patchLocations);
+ void setGroupCacheOverrides(const std::vector<binary_format::DyldCacheOverride>& cacheOverrides);
+ void addImageIsOverride(binary_format::ImageRef replacer, binary_format::ImageRef replacee);
+
+ uint32_t addIndirectGroupNum(uint32_t groupNum);
+
+ uint32_t addString(const char* str);
+ void alignStringPool();
+
+ uint32_t imageDependentsCount(uint32_t imageIndex) const;
+ binary_format::ImageRef imageDependent(uint32_t imageIndex, uint32_t depIndex) const;
+
+private:
+ struct InitializerInfo {
+ std::vector<uint32_t> offsetsInImage;
+ std::vector<binary_format::ImageRef> initBeforeImages;
+ };
+
+ uint32_t imageCount() const;
+ binary_format::Image& imageByIndex(uint32_t);
+ const binary_format::Image& imageByIndex(uint32_t) const;
+ std::vector<uint8_t> makeFixupOpcodes(const FixUp* start, const FixUp* end, uint32_t pageStartSegmentOffset, std::map<uint32_t, TargetSymbolValue>&);
+ void makeDataFixupMapAndOrdinalTable(std::vector<uint8_t>& fixupMap, std::vector<TargetSymbolValue>& ordinalTable);
+ void computeInitializerOrdering(uint32_t imageIndex);
+ uint32_t addUniqueInitList(const std::vector<binary_format::ImageRef>& initBefore);
+ void layoutBinary(binary_format::ImageGroup* grp) const;
+
+ const bool _isDiskImage;
+ const bool _is64;
+ const uint16_t _groupNum;
+ const uint32_t _pageSize;
+ bool _dylibsExpectedOnDisk;
+ bool _imageFileInfoIsCdHash;
+ std::vector<binary_format::CachedImage> _images;
+ std::vector<binary_format::DiskImage> _diskImages;
+ std::vector<binary_format::AliasEntry> _aliases;
+ std::vector<uint64_t> _segmentPool;
+ std::vector<binary_format::ImageRef> _dependentsPool;
+ std::vector<uint32_t> _initializerOffsets;
+ std::vector<binary_format::ImageRef> _initializerBeforeLists;
+ std::vector<uint32_t> _dofOffsets;
+ std::vector<TargetSymbolValue> _targetsPool;
+ ContentBuffer _fixupsPool;
+ std::vector<binary_format::PatchTable> _patchPool;
+ std::vector<binary_format::PatchOffset> _patchLocationPool;
+ std::vector<binary_format::DyldCacheOverride>_dyldCacheSymbolOverridePool;
+ std::vector<binary_format::ImageRefOverride> _imageOverridePool;
+ std::vector<uint32_t> _indirectGroupNumPool;
+ std::unordered_map<uint32_t, uint32_t> _indirectGroupNumPoolExisting;
+ std::vector<char> _stringPool;
+ std::unordered_map<std::string, uint32_t> _stringPoolExisting;
+};
+
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+
+#endif // LaunchCacheWriter_h
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <fcntl.h>
+#include <sys/dtrace.h>
+#include <sys/errno.h>
+#include <unistd.h>
+#include <System/sys/mman.h>
+#include <System/sys/csr.h>
+#include <System/machine/cpu_capabilities.h>
+#include <bootstrap.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <sandbox.h>
+#include <sandbox/private.h>
+#include <dispatch/dispatch.h>
+
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "Logging.h"
+#include "Loading.h"
+#include "MachOParser.h"
+#include "dyld.h"
+#include "dyld_cache_format.h"
+
+extern "C" {
+ #include "closuredProtocol.h"
+}
+
+namespace dyld {
+ void log(const char* m, ...);
+}
+
+namespace dyld3 {
+namespace loader {
+
+#if DYLD_IN_PROCESS
+
+static bool sandboxBlocked(const char* path, const char* kind)
+{
+#if BUILDING_LIBDYLD || !TARGET_IPHONE_SIMULATOR
+ sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+ return ( sandbox_check(getpid(), kind, filter, path) > 0 );
+#else
+ // sandbox calls not yet supported in dyld_sim
+ return false;
+#endif
+}
+
+static bool sandboxBlockedMmap(const char* path)
+{
+ return sandboxBlocked(path, "file-map-executable");
+}
+
+static bool sandboxBlockedOpen(const char* path)
+{
+ return sandboxBlocked(path, "file-read-data");
+}
+
+static bool sandboxBlockedStat(const char* path)
+{
+ return sandboxBlocked(path, "file-read-metadata");
+}
+
+#if TARGET_OS_WATCH
+static uint64_t pageAlign(uint64_t value)
+{
+ return (value + 4095) & (-4096);
+}
+#endif
+
+static void updateSliceOffset(uint64_t& sliceOffset, uint64_t codeSignEndOffset, size_t fileLen)
+{
+#if TARGET_OS_WATCH
+ if ( sliceOffset != 0 ) {
+ if ( pageAlign(codeSignEndOffset) == pageAlign(fileLen) ) {
+ // cache builder saw fat file, but file is now thin
+ sliceOffset = 0;
+ return;
+ }
+ }
+#endif
+}
+
+static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagnostics& diag, LogFunc log_loads, LogFunc log_segments)
+{
+ uint64_t sliceOffset = image.sliceOffsetInFile();
+ const uint64_t totalVMSize = image.vmSizeToMap();
+ const uint32_t codeSignFileOffset = image.asDiskImage()->codeSignFileOffset;
+ const uint32_t codeSignFileSize = image.asDiskImage()->codeSignFileSize;
+
+ // open file
+ int fd = ::open(image.path(), O_RDONLY, 0);
+ if ( fd == -1 ) {
+ int openErr = errno;
+ if ( (openErr == EPERM) && sandboxBlockedOpen(image.path()) )
+ diag.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image.path());
+ else
+ diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image.path(), openErr);
+ return nullptr;
+ }
+
+ // get file info
+ struct stat statBuf;
+#if TARGET_IPHONE_SIMULATOR
+ if ( stat(image.path(), &statBuf) != 0 ) {
+#else
+ if ( fstat(fd, &statBuf) != 0 ) {
+#endif
+ int statErr = errno;
+ if ( (statErr == EPERM) && sandboxBlockedStat(image.path()) )
+ diag.error("file system sandbox blocked stat(\"%s\")", image.path());
+ else
+ diag.error("stat(\"%s\") failed with errno=%d", image.path(), statErr);
+ close(fd);
+ return nullptr;
+ }
+
+ // verify file has not changed since closure was built
+ if ( image.validateUsingModTimeAndInode() ) {
+ if ( (statBuf.st_mtime != image.fileModTime()) || (statBuf.st_ino != image.fileINode()) ) {
+ diag.error("file mtime/inode changed since closure was built for '%s'", image.path());
+ close(fd);
+ return nullptr;
+ }
+ }
+
+ // handle OS dylibs being thinned after closure was built
+ if ( image.group().groupNum() == 1 )
+ updateSliceOffset(sliceOffset, codeSignFileOffset+codeSignFileSize, (size_t)statBuf.st_size);
+
+ // register code signature
+ uint64_t coveredCodeLength = UINT64_MAX;
+ if ( codeSignFileOffset != 0 ) {
+ fsignatures_t siginfo;
+ siginfo.fs_file_start = sliceOffset; // start of mach-o slice in fat file
+ siginfo.fs_blob_start = (void*)(long)(codeSignFileOffset); // start of CD in mach-o file
+ siginfo.fs_blob_size = codeSignFileSize; // size of CD
+ int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
+ if ( result == -1 ) {
+ int errnoCopy = errno;
+ if ( (errnoCopy == EPERM) || (errnoCopy == EBADEXEC) ) {
+ diag.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
+ errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path());
+ }
+ else {
+ diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
+ errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path());
+ }
+ close(fd);
+ return nullptr;
+ }
+ coveredCodeLength = siginfo.fs_file_start;
+ if ( coveredCodeLength < image.asDiskImage()->codeSignFileOffset ) {
+ diag.error("code signature does not cover entire file up to signature");
+ close(fd);
+ return nullptr;
+ }
+
+ // <rdar://problem/32684903> always call F_CHECK_LV to preflight
+ fchecklv checkInfo;
+ char messageBuffer[512];
+ messageBuffer[0] = '\0';
+ checkInfo.lv_file_start = sliceOffset;
+ checkInfo.lv_error_message_size = sizeof(messageBuffer);
+ checkInfo.lv_error_message = messageBuffer;
+ int res = fcntl(fd, F_CHECK_LV, &checkInfo);
+ if ( res == -1 ) {
+ diag.error("code signature in (%s) not valid for use in process: %s", image.path(), messageBuffer);
+ close(fd);
+ return nullptr;
+ }
+ }
+
+ // reserve address range
+ vm_address_t loadAddress = 0;
+ kern_return_t r = vm_allocate(mach_task_self(), &loadAddress, (vm_size_t)totalVMSize, VM_FLAGS_ANYWHERE);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
+ close(fd);
+ return nullptr;
+ }
+
+ if ( sliceOffset != 0 )
+ log_segments("dyld: Mapping %s (slice offset=%llu)\n", image.path(), sliceOffset);
+ else
+ log_segments("dyld: Mapping %s\n", image.path());
+
+ // map each segment
+ __block bool mmapFailure = false;
+ __block const uint8_t* codeSignatureStartAddress = nullptr;
+ __block const uint8_t* linkeditEndAddress = nullptr;
+ __block bool mappedFirstSegment = false;
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ // <rdar://problem/32363581> Mapping zero filled segments fails with mmap of size 0
+ if ( fileSize == 0 )
+ return;
+ void* segAddress = mmap((void*)(loadAddress+vmOffset), fileSize, permissions, MAP_FIXED | MAP_PRIVATE, fd, sliceOffset+fileOffset);
+ int mmapErr = errno;
+ if ( segAddress == MAP_FAILED ) {
+ if ( mmapErr == EPERM ) {
+ if ( sandboxBlockedMmap(image.path()) )
+ diag.error("file system sandbox blocked mmap() of '%s'", image.path());
+ else
+ diag.error("code signing blocked mmap() of '%s'", image.path());
+ }
+ else {
+ diag.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress+vmOffset, fileSize, mmapErr, image.path());
+ }
+ mmapFailure = true;
+ stop = true;
+ }
+ else if ( codeSignFileOffset > fileOffset ) {
+ codeSignatureStartAddress = (uint8_t*)segAddress + (codeSignFileOffset-fileOffset);
+ linkeditEndAddress = (uint8_t*)segAddress + vmSize;
+ }
+ // sanity check first segment is mach-o header
+ if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
+ mappedFirstSegment = true;
+ if ( !MachOParser::isMachO(diag, segAddress, fileSize) ) {
+ mmapFailure = true;
+ stop = true;
+ }
+ }
+ if ( !mmapFailure ) {
+ MachOParser parser((mach_header*)loadAddress);
+ log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex),
+ (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+ (long)segAddress, (long)segAddress+vmSize-1);
+ }
+ });
+ if ( mmapFailure ) {
+ vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ close(fd);
+ return nullptr;
+ }
+
+ // close file
+ close(fd);
+
+ #if BUILDING_LIBDYLD
+ // verify file has not changed since closure was built by checking code signature has not changed
+ if ( image.validateUsingCdHash() ) {
+ if ( codeSignatureStartAddress == nullptr ) {
+ diag.error("code signature missing");
+ }
+ else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) {
+ diag.error("code signature extends beyond end of __LINKEDIT");
+ }
+ else {
+ uint8_t cdHash[20];
+ if ( MachOParser::cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHash) ) {
+ if ( memcmp(image.cdHash16(), cdHash, 16) != 0 )
+ diag.error("code signature changed since closure was built");
+ }
+ else{
+ diag.error("code signature format invalid");
+ }
+ }
+ if ( diag.hasError() ) {
+ vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ return nullptr;
+ }
+ }
+#endif
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ // tell kernel about fairplay encrypted regions
+ uint32_t fpTextOffset;
+ uint32_t fpSize;
+ if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
+ const mach_header* mh = (mach_header*)loadAddress;
+ int result = mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
+ diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
+ vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ return nullptr;
+ }
+#endif
+
+ log_loads("dyld: load %s\n", image.path());
+
+ return (mach_header*)loadAddress;
+}
+
+
+void unmapImage(const launch_cache::binary_format::Image* binImage, const mach_header* loadAddress)
+{
+ assert(loadAddress != nullptr);
+ launch_cache::Image image(binImage);
+ vm_deallocate(mach_task_self(), (vm_address_t)loadAddress, (vm_size_t)(image.vmSizeToMap()));
+}
+
+
+static void applyFixupsToImage(Diagnostics& diag, const mach_header* imageMH, const launch_cache::binary_format::Image* imageData,
+ launch_cache::TargetSymbolValue::LoadedImages& imageResolver, LogFunc log_fixups)
+{
+ launch_cache::Image image(imageData);
+ MachOParser imageParser(imageMH);
+ // Note, these are cached here to avoid recalculating them on every loop iteration
+ const launch_cache::ImageGroup& imageGroup = image.group();
+ const char* leafName = image.leafName();
+ intptr_t slide = imageParser.getSlide();
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
+ if ( !image.segmentHasFixups(segIndex) )
+ return;
+ const launch_cache::MemoryRange segContent = { (char*)imageMH + vmOffset, vmSize };
+ #if __i386__
+ bool textRelocs = ((protections & VM_PROT_WRITE) == 0);
+ if ( textRelocs ) {
+ kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, VM_PROT_WRITE | VM_PROT_READ);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("vm_protect() failed trying to make text segment writable, result=%d", r);
+ return;
+ }
+ }
+ #else
+ if ( (protections & VM_PROT_WRITE) == 0 ) {
+ diag.error("fixups found in non-writable segment of %s", image.path());
+ return;
+ }
+ #endif
+ image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, launch_cache::Image::FixupKind kind, launch_cache::TargetSymbolValue targetValue, bool& stop) {
+ if ( segOffset > segContent.size ) {
+ diag.error("fixup is past end of segment. segOffset=0x%0llX, segSize=0x%0llX, segIndex=%d", segOffset, segContent.size, segIndex);
+ stop = true;
+ return;
+ }
+ uintptr_t* fixUpLoc = (uintptr_t*)((char*)(segContent.address) + segOffset);
+ uintptr_t value;
+ #if __i386__
+ uint32_t rel32;
+ uint8_t* jumpSlot;
+ #endif
+ //dyld::log("fixup loc=%p\n", fixUpLoc);
+ switch ( kind ) {
+ #if __LP64__
+ case launch_cache::Image::FixupKind::rebase64:
+ #else
+ case launch_cache::Image::FixupKind::rebase32:
+ #endif
+ *fixUpLoc += slide;
+ log_fixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+ break;
+ #if __LP64__
+ case launch_cache::Image::FixupKind::bind64:
+ #else
+ case launch_cache::Image::FixupKind::bind32:
+ #endif
+ value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+ log_fixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+ *fixUpLoc = value;
+ break;
+ #if __i386__
+ case launch_cache::Image::FixupKind::rebaseText32:
+ log_fixups("dyld: text fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+ *fixUpLoc += slide;
+ break;
+ case launch_cache::Image::FixupKind::bindText32:
+ value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+ log_fixups("dyld: text fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+ *fixUpLoc = value;
+ break;
+ case launch_cache::Image::FixupKind::bindTextRel32:
+ // CALL instruction uses pc-rel value
+ value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+ log_fixups("dyld: CALL fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, (value - (uintptr_t)(fixUpLoc)));
+ *fixUpLoc = (value - (uintptr_t)(fixUpLoc));
+ break;
+ case launch_cache::Image::FixupKind::bindImportJmp32:
+ // JMP instruction in __IMPORT segment uses pc-rel value
+ jumpSlot = (uint8_t*)fixUpLoc;
+ value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+ rel32 = (value - ((uintptr_t)(fixUpLoc)+5));
+ log_fixups("dyld: JMP fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, rel32);
+ jumpSlot[0] = 0xE9; // JMP rel32
+ jumpSlot[1] = rel32 & 0xFF;
+ jumpSlot[2] = (rel32 >> 8) & 0xFF;
+ jumpSlot[3] = (rel32 >> 16) & 0xFF;
+ jumpSlot[4] = (rel32 >> 24) & 0xFF;
+ break;
+ #endif
+ default:
+ diag.error("unknown fixup kind %d", kind);
+ }
+ if ( diag.hasError() )
+ stop = true;
+ });
+ #if __i386__
+ if ( textRelocs ) {
+ kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, protections);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("vm_protect() failed trying to make text segment non-writable, result=%d", r);
+ return;
+ }
+ }
+ #endif
+ });
+}
+
+
+
+class VIS_HIDDEN CurrentLoadImages : public launch_cache::TargetSymbolValue::LoadedImages
+{
+public:
+ CurrentLoadImages(launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheAddr)
+ : _dyldCacheLoadAddress(cacheAddr), _images(images) { }
+
+ virtual const uint8_t* dyldCacheLoadAddressForImage();
+ virtual const mach_header* loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup);
+ virtual void forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop));
+ virtual void setAsNeverUnload(uint32_t anIndex) { _images[anIndex].neverUnload = true; }
+private:
+ const uint8_t* _dyldCacheLoadAddress;
+ launch_cache::DynArray<ImageInfo>& _images;
+};
+
+const uint8_t* CurrentLoadImages::dyldCacheLoadAddressForImage()
+{
+ return _dyldCacheLoadAddress;
+}
+
+const mach_header* CurrentLoadImages::loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup)
+{
+ __block const mach_header* result = nullptr;
+ forEachImage(^(uint32_t anIndex, const launch_cache::binary_format::Image* imageData, const mach_header* mh, bool& stop) {
+ launch_cache::Image image(imageData);
+ launch_cache::ImageGroup imageGroup = image.group();
+ if ( imageGroup.groupNum() != groupNum )
+ return;
+ if ( imageGroup.indexInGroup(imageData) == indexInGroup ) {
+ result = mh;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+void CurrentLoadImages::forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop))
+{
+ bool stop = false;
+ for (int i=0; i < _images.count(); ++i) {
+ ImageInfo& info = _images[i];
+ handler(i, info.imageData, info.loadAddress, stop);
+ if ( stop )
+ break;
+ }
+}
+
+struct DOFInfo {
+ const void* dof;
+ const mach_header* imageHeader;
+ const char* imageShortName;
+};
+
+static void registerDOFs(const DOFInfo* dofs, uint32_t dofSectionCount, LogFunc log_dofs)
+{
+ if ( dofSectionCount != 0 ) {
+ int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+ if ( fd < 0 ) {
+ log_dofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n");
+ }
+ else {
+ // allocate a buffer on the stack for the variable length dof_ioctl_data_t type
+ uint8_t buffer[sizeof(dof_ioctl_data_t) + dofSectionCount*sizeof(dof_helper_t)];
+ dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer;
+
+ // fill in buffer with one dof_helper_t per DOF section
+ ioctlData->dofiod_count = dofSectionCount;
+ for (unsigned int i=0; i < dofSectionCount; ++i) {
+ strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN);
+ ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof);
+ ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof);
+ }
+
+ // tell kernel about all DOF sections en mas
+ // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel
+ user_addr_t val = (user_addr_t)(unsigned long)ioctlData;
+ if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) {
+ // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field.
+ // Note, the closure marked the image as being never unload, so we don't need to keep the ID around
+ // or support unregistering it later.
+ for (unsigned int i=0; i < dofSectionCount; ++i) {
+ log_dofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
+ dofs[i].dof, dofs[i].imageShortName, (int)(ioctlData->dofiod_helpers[i].dofhp_dof));
+ }
+ }
+ else {
+ //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n");
+ }
+ close(fd);
+ }
+ }
+}
+
+
+void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
+ LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs)
+{
+ // scan array and map images not already loaded
+ for (int i=0; i < images.count(); ++i) {
+ ImageInfo& info = images[i];
+ const dyld3::launch_cache::Image image(info.imageData);
+ if ( info.loadAddress != nullptr ) {
+ // log main executable's segments
+ if ( (info.groupNum == 2) && (info.loadAddress->filetype == MH_EXECUTE) && !info.previouslyFixedUp ) {
+ if ( log_segments("dyld: mapped by kernel %s\n", image.path()) ) {
+ MachOParser parser(info.loadAddress);
+ image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ uint64_t start = (long)info.loadAddress + vmOffset;
+ uint64_t end = start+vmSize-1;
+ if ( (segIndex == 0) && (permissions == 0) ) {
+ start = 0;
+ }
+ log_segments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", parser.segmentName(segIndex),
+ (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+ start, end);
+ });
+ }
+ }
+ // skip over ones already loaded
+ continue;
+ }
+ if ( image.isDiskImage() ) {
+ //dyld::log("need to load image[%d] %s\n", i, image.path());
+ info.loadAddress = mapImage(image, diag, log_loads, log_segments);
+ if ( diag.hasError() ) {
+ break; // out of for loop
+ }
+ info.justMapped = true;
+ }
+ else {
+ bool expectedOnDisk = image.group().dylibsExpectedOnDisk();
+ bool overridableDylib = image.overridableDylib();
+ if ( expectedOnDisk || overridableDylib ) {
+ struct stat statBuf;
+ if ( ::stat(image.path(), &statBuf) == 0 ) {
+ if ( expectedOnDisk ) {
+ // macOS case: verify dylib file info matches what it was when cache was built
+ if ( image.fileModTime() != statBuf.st_mtime ) {
+ diag.error("cached dylib mod-time has changed, dylib cache has: 0x%08llX, file has: 0x%08lX, for: %s", image.fileModTime(), (long)statBuf.st_mtime, image.path());
+ break; // out of for loop
+ }
+ if ( image.fileINode() != statBuf.st_ino ) {
+ diag.error("cached dylib inode has changed, dylib cache has: 0x%08llX, file has: 0x%08llX, for: %s", image.fileINode(), statBuf.st_ino, image.path());
+ break; // out of for loop
+ }
+ }
+ else {
+ // iOS internal: dylib override installed
+ diag.error("cached dylib overridden: %s", image.path());
+ break; // out of for loop
+ }
+ }
+ else {
+ if ( expectedOnDisk ) {
+ // macOS case: dylib that existed when cache built no longer exists
+ diag.error("missing cached dylib: %s", image.path());
+ break; // out of for loop
+ }
+ }
+ }
+ info.loadAddress = (mach_header*)(cacheLoadAddress + image.cacheOffset());
+ info.justUsedFromDyldCache = true;
+ if ( log_segments("dyld: Using from dyld cache %s\n", image.path()) ) {
+ MachOParser parser(info.loadAddress);
+ image.forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+ log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex),
+ (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+ (long)cacheLoadAddress+vmOffset, (long)cacheLoadAddress+vmOffset+vmSize-1);
+ });
+ }
+ }
+ }
+ if ( diag.hasError() ) {
+ // back out and unmapped images all loaded so far
+ for (uint32_t j=0; j < images.count(); ++j) {
+ ImageInfo& anInfo = images[j];
+ if ( anInfo.justMapped )
+ unmapImage(anInfo.imageData, anInfo.loadAddress);
+ anInfo.loadAddress = nullptr;
+ }
+ return;
+ }
+
+ // apply fixups
+ CurrentLoadImages fixupHelper(images, cacheLoadAddress);
+ for (int i=0; i < images.count(); ++i) {
+ ImageInfo& info = images[i];
+ // images in shared cache do not need fixups applied
+ launch_cache::Image image(info.imageData);
+ if ( !image.isDiskImage() )
+ continue;
+ // previously loaded images were previously fixed up
+ if ( info.previouslyFixedUp )
+ continue;
+ //dyld::log("apply fixups to mh=%p, path=%s\n", info.loadAddress, Image(info.imageData).path());
+ dyld3::loader::applyFixupsToImage(diag, info.loadAddress, info.imageData, fixupHelper, log_fixups);
+ if ( diag.hasError() )
+ break;
+ }
+
+ // Record dtrace DOFs
+ // if ( /* FIXME! register dofs */ )
+ {
+ __block uint32_t dofCount = 0;
+ for (int i=0; i < images.count(); ++i) {
+ ImageInfo& info = images[i];
+ launch_cache::Image image(info.imageData);
+ // previously loaded images were previously fixed up
+ if ( info.previouslyFixedUp )
+ continue;
+ image.forEachDOF(nullptr, ^(const void* section) {
+ // DOFs cause the image to be never-unload
+ assert(image.neverUnload());
+ ++dofCount;
+ });
+ }
+
+ // struct RegisteredDOF { const mach_header* mh; int registrationID; };
+ DOFInfo dofImages[dofCount];
+ __block DOFInfo* dofImagesBase = dofImages;
+ dofCount = 0;
+ for (int i=0; i < images.count(); ++i) {
+ ImageInfo& info = images[i];
+ launch_cache::Image image(info.imageData);
+ // previously loaded images were previously fixed up
+ if ( info.previouslyFixedUp )
+ continue;
+ image.forEachDOF(info.loadAddress, ^(const void* section) {
+ DOFInfo dofInfo;
+ dofInfo.dof = section;
+ dofInfo.imageHeader = info.loadAddress;
+ dofInfo.imageShortName = image.leafName();
+ dofImagesBase[dofCount++] = dofInfo;
+ });
+ }
+ registerDOFs(dofImages, dofCount, log_dofs);
+ }
+}
+
+#if BUILDING_DYLD
+void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
+{
+ int fd = dyld::my_open(path, O_RDONLY, 0);
+ if ( fd != -1 ) {
+ struct stat statBuf;
+ if ( fstat(fd, &statBuf) == 0 ) {
+ const char* lines = (const char*)mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( lines != MAP_FAILED ) {
+ bool stop = false;
+ const char* const eof = &lines[statBuf.st_size];
+ for (const char* s = lines; s < eof; ++s) {
+ char lineBuffer[MAXPATHLEN];
+ char* t = lineBuffer;
+ char* tEnd = &lineBuffer[MAXPATHLEN];
+ while ( (s < eof) && (t != tEnd) ) {
+ if ( *s == '\n' )
+ break;
+ *t++ = *s++;
+ }
+ *t = '\0';
+ lineHandler(lineBuffer, stop);
+ if ( stop )
+ break;
+ }
+ munmap((void*)lines, (size_t)statBuf.st_size);
+ }
+ }
+ close(fd);
+ }
+}
+
+
+bool internalInstall()
+{
+#if TARGET_IPHONE_SIMULATOR
+ return false;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+ uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
+ return ( (devFlags & 1) == 1 );
+#else
+ return ( csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0 );
+#endif
+}
+
+/* Checks to see if there are any args that impact dyld. These args
+ * can be set sevaral ways. These will only be honored on development
+ * and Apple Internal builds.
+ *
+ * First the existence of a file is checked for:
+ * /S/L/C/com.apple.dyld/dyld-bootargs
+ * If it exists it will be mapped and scanned line by line. If the executable
+ * exists in the file then the arguments on its line will be applied. "*" may
+ * be used a wildcard to represent all apps. First matching line will be used,
+ * the wild card must be one the last line. Additionally, lines must end with
+ * a "\n"
+ *
+ *
+ * SAMPLE FILE:
+
+ /bin/ls:force_dyld2=1
+ /usr/bin/sw_vers:force_dyld2=1
+*:force_dyld3=1
+EOL
+
+ If no file exists then the kernel boot-args will be scanned.
+ */
+bool bootArgsContains(const char* arg)
+{
+ //FIXME: Use strnstr(). Unfortunately we are missing an imp libc.
+#if TARGET_IPHONE_SIMULATOR
+ return false;
+#else
+ // don't check for boot-args on customer installs
+ if ( !internalInstall() )
+ return false;
+
+ char pathBuffer[MAXPATHLEN+1];
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
+#else
+ strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
+#endif
+ strlcat(pathBuffer, "dyld-bootargs", MAXPATHLEN+1);
+ __block bool result = false;
+ forEachLineInFile(pathBuffer, ^(const char* line, bool& stop) {
+ const char* delim = strchr(line, ':');
+ if ( delim == nullptr )
+ return;
+ char binary[MAXPATHLEN];
+ char options[MAXPATHLEN];
+ strlcpy(binary, line, MAXPATHLEN);
+ binary[delim-line] = '\0';
+ strlcpy(options, delim+1, MAXPATHLEN);
+ if ( (strcmp(dyld::getExecutablePath(), binary) == 0) || (strcmp("*", binary) == 0) ) {
+ result = (strstr(options, arg) != nullptr);
+ return;
+ }
+ });
+
+ // get length of full boot-args string
+ size_t len;
+ if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 )
+ return false;
+
+ // get copy of boot-args string
+ char bootArgsBuffer[len];
+ if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 )
+ return false;
+
+ // return true if 'arg' is a sub-string of boot-args
+ return (strstr(bootArgsBuffer, arg) != nullptr);
+#endif
+}
+#endif
+
+#if BUILDING_LIBDYLD
+// hack because libdyld.dylib should not link with libc++.dylib
+extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));
+void __cxa_pure_virtual()
+{
+ abort();
+}
+#endif
+
+
+#endif // DYLD_IN_PROCESS
+
+} // namespace loader
+} // namespace dyld3
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_LOADING_H__
+#define __DYLD_LOADING_H__
+
+#include <string.h>
+#include <stdint.h>
+#include <mach/mach.h>
+#include <_simple.h>
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "MachOParser.h"
+#include "ClosureBuffer.h"
+
+
+
+namespace dyld3 {
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input);
+
+namespace loader {
+
+struct ImageInfo
+{
+ const launch_cache::binary_format::Image* imageData;
+ const mach_header* loadAddress;
+ uint32_t groupNum;
+ uint32_t indexInGroup;
+ bool previouslyFixedUp;
+ bool justMapped;
+ bool justUsedFromDyldCache;
+ bool neverUnload;
+};
+
+
+#if DYLD_IN_PROCESS
+
+typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
+
+void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
+ LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs) VIS_HIDDEN;
+
+
+void unmapImage(const launch_cache::binary_format::Image* image, const mach_header* loadAddress) VIS_HIDDEN;
+
+#if BUILDING_DYLD
+bool bootArgsContains(const char* arg) VIS_HIDDEN;
+bool internalInstall();
+void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop));
+#endif
+#endif
+
+} // namespace loader
+} // namespace dyld3
+
+
+#endif // __DYLD_LOADING_H__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <_simple.h>
+
+#include "Logging.h"
+
+
+namespace dyld3 {
+
+
+static bool sVerboseLoading = false;
+static bool sVerboseInitializers = false;
+static bool sVerboseSegments = false;
+static bool sVerboseAPIs = false;
+static bool sVerboseNotifications = false;
+static bool sVerboseFixups = false;
+static bool sVerboseDOFs = false;
+
+static void vlog_default(const char* format, va_list list)
+{
+ _simple_vdprintf(2, format, list);
+}
+
+static void (*sLogFunction)(const char* format, va_list list) = &vlog_default;
+static void (*sHaltFunction)(const char* message) __attribute__((noreturn)) = nullptr;
+
+void halt(const char* message)
+{
+ (*sHaltFunction)(message);
+}
+
+static void vlog(const char* format, va_list list)
+{
+ (*sLogFunction)(format, list);
+}
+
+bool log_loads(const char* format, ...)
+{
+ if ( !sVerboseLoading )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_segments(const char* format, ...)
+{
+ if ( !sVerboseSegments )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_fixups(const char* format, ...)
+{
+ if ( !sVerboseFixups )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_initializers(const char* format, ...)
+{
+ if ( !sVerboseInitializers )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_apis(const char* format, ...)
+{
+ if ( !sVerboseAPIs )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_notifications(const char* format, ...)
+{
+ if ( !sVerboseNotifications )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+bool log_dofs(const char* format, ...)
+{
+ if ( !sVerboseDOFs )
+ return false;
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+
+
+void setLoggingFromEnvs(const char* envp[])
+{
+ for(const char** p = envp; *p != NULL; p++) {
+ const char* keyEqualsValue = *p;
+ const char* equals = strchr(keyEqualsValue, '=');
+ if ( equals != NULL ) {
+ //const char* value = &equals[1];
+ const size_t keyLen = equals-keyEqualsValue;
+ char key[keyLen+1];
+ strncpy(key, keyEqualsValue, keyLen);
+ key[keyLen] = '\0';
+ if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) {
+ sVerboseLoading = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) {
+ sVerboseSegments = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) {
+ sVerboseInitializers = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) {
+ sVerboseAPIs = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_NOTIFICATIONS") == 0 ) {
+ sVerboseNotifications = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) {
+ sVerboseFixups = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) {
+ sVerboseDOFs = true;
+ }
+ }
+ }
+}
+
+void setLoggingFunction(void (*func)(const char* format, va_list list))
+{
+ sLogFunction = func;
+}
+
+void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)))
+{
+ sHaltFunction = func;
+}
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_LOGGING_H__
+#define __DYLD_LOGGING_H__
+
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+
+namespace dyld3 {
+
+
+bool log_loads(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_apis(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_segments(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_initializers(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_fixups(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_notifications(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_dofs(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+
+void halt(const char* message) __attribute((noreturn)) VIS_HIDDEN ;
+
+
+// only called during libdyld set up
+void setLoggingFromEnvs(const char* envp[]) VIS_HIDDEN ;
+void setLoggingFunction(void (*func)(const char* format, va_list list)) VIS_HIDDEN;
+void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn))) VIS_HIDDEN;
+
+
+
+
+} // namespace dyld3
+
+#endif // __DYLD_LOGGING_H__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <rootless.h>
+#include <dirent.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/fat.h>
+#include <mach-o/reloc.h>
+#include <mach-o/dyld_priv.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#if !DYLD_IN_PROCESS
+#include <dlfcn.h>
+#endif
+
+#include "MachOParser.h"
+#include "Logging.h"
+#include "CodeSigningTypes.h"
+#include "DyldSharedCache.h"
+#include "Trie.hpp"
+
+#if DYLD_IN_PROCESS
+ #include "APIs.h"
+#else
+ #include "StringUtils.h"
+#endif
+
+
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
+
+#ifndef LC_BUILD_VERSION
+ #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+ /*
+ * The build_version_command contains the min OS version on which this
+ * binary was built to run for its platform. The list of known platforms and
+ * tool values following it.
+ */
+ struct build_version_command {
+ uint32_t cmd; /* LC_BUILD_VERSION */
+ uint32_t cmdsize; /* sizeof(struct build_version_command) plus */
+ /* ntools * sizeof(struct build_tool_version) */
+ uint32_t platform; /* platform */
+ uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t ntools; /* number of tool entries following this */
+ };
+
+ struct build_tool_version {
+ uint32_t tool; /* enum for the tool */
+ uint32_t version; /* version number of the tool */
+ };
+
+ /* Known values for the platform field above. */
+ #define PLATFORM_MACOS 1
+ #define PLATFORM_IOS 2
+ #define PLATFORM_TVOS 3
+ #define PLATFORM_WATCHOS 4
+ #define PLATFORM_BRIDGEOS 5
+
+ /* Known values for the tool field above. */
+ #define TOOL_CLANG 1
+ #define TOOL_SWIFT 2
+ #define TOOL_LD 3
+#endif
+
+
+namespace dyld3 {
+
+
+bool FatUtil::isFatFile(const void* fileStart)
+{
+ const fat_header* fileStartAsFat = (fat_header*)fileStart;
+ return ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) );
+}
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+static bool greaterThanAddOrOverflow(uint32_t addLHS, uint32_t addRHS, T b) {
+ return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+static bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
+ return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+void FatUtil::forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop))
+{
+ const fat_header* fh = (fat_header*)fileContent;
+ if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ diag.error("not a fat file");
+ return;
+ }
+
+ if ( OSSwapBigToHostInt32(fh->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+ diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(fh->nfat_arch));
+ }
+ const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
+ bool stop = false;
+ for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
+ uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
+ uint32_t offset = OSSwapBigToHostInt32(archs[i].offset);
+ uint32_t len = OSSwapBigToHostInt32(archs[i].size);
+ if (greaterThanAddOrOverflow(offset, len, fileLen)) {
+ diag.error("slice %d extends beyond end of file", i);
+ return;
+ }
+ callback(cpuType, cpuSubType, (uint8_t*)fileContent+offset, len, stop);
+ if ( stop )
+ break;
+ }
+}
+
+#if !DYLD_IN_PROCESS
+bool FatUtil::isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice)
+{
+ missingSlice = false;
+ if ( !isFatFile(fileContent) )
+ return false;
+
+ __block bool found = false;
+ forEachSlice(diag, fileContent, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+ std::string sliceArchName = MachOParser::archName(sliceCpuType, sliceCpuSubType);
+ if ( sliceArchName == archName ) {
+ sliceOffset = (char*)sliceStart - (char*)fileContent;
+ sliceLen = sliceSize;
+ found = true;
+ stop = true;
+ }
+ });
+ if ( diag.hasError() )
+ return false;
+
+ if ( !found )
+ missingSlice = true;
+
+ // when looking for x86_64h fallback to x86_64
+ if ( !found && (archName == "x86_64h") )
+ return isFatFileWithSlice(diag, fileContent, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
+
+ return found;
+}
+
+#endif
+
+MachOParser::MachOParser(const mach_header* mh, bool dyldCacheIsRaw)
+{
+#if DYLD_IN_PROCESS
+ // assume all in-process mach_headers are real loaded images
+ _data = (long)mh;
+#else
+ if (mh == nullptr)
+ return;
+ _data = (long)mh;
+ if ( (mh->flags & 0x80000000) == 0 ) {
+ // asssume out-of-process mach_header not in a dyld cache are raw mapped files
+ _data |= 1;
+ }
+ else {
+ // out-of-process mach_header in a dyld cache are not raw, but cache may be raw
+ if ( dyldCacheIsRaw )
+ _data |= 2;
+ }
+#endif
+}
+
+const mach_header* MachOParser::header() const
+{
+ return (mach_header*)(_data & -4);
+}
+
+// "raw" means the whole mach-o file was mapped as one contiguous region
+// not-raw means the the mach-o file was mapped like dyld does - with zero fill expansion
+bool MachOParser::isRaw() const
+{
+ return (_data & 1);
+}
+
+// A raw dyld cache is when the whole dyld cache file is mapped in one contiguous region
+// not-raw manes the dyld cache was mapped as it is at runtime with padding between regions
+bool MachOParser::inRawCache() const
+{
+ return (_data & 2);
+}
+
+uint32_t MachOParser::fileType() const
+{
+ return header()->filetype;
+}
+
+bool MachOParser::inDyldCache() const
+{
+ return (header()->flags & 0x80000000);
+}
+
+bool MachOParser::hasThreadLocalVariables() const
+{
+ return (header()->flags & MH_HAS_TLV_DESCRIPTORS);
+}
+
+Platform MachOParser::platform() const
+{
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+ if ( getPlatformAndVersion(&platform, &minOS, &sdk) )
+ return platform;
+
+ // old binary with no explict load command to mark platform, look at arch
+ switch ( header()->cputype ) {
+ case CPU_TYPE_X86_64:
+ case CPU_TYPE_I386:
+ return Platform::macOS;
+ case CPU_TYPE_ARM64:
+ case CPU_TYPE_ARM:
+ return Platform::iOS;
+ }
+ return Platform::macOS;
+}
+
+
+#if !DYLD_IN_PROCESS
+
+const MachOParser::ArchInfo MachOParser::_s_archInfos[] = {
+ { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
+ { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H },
+ { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
+ { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
+ { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E },
+ { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
+ { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
+ { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
+};
+
+bool MachOParser::isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables)
+{
+ // must start with mach-o magic value
+ const mach_header* mh = (const mach_header*)fileContent;
+ if ( (mh->magic != MH_MAGIC) && (mh->magic != MH_MAGIC_64) ) {
+ diag.warning("could not use '%s' because it is not a mach-o file", pathOpened.c_str());
+ return false;
+ }
+
+ // must match requested architecture if specified
+ if (!archName.empty() && !isArch(mh, archName)) {
+ // except when looking for x86_64h, fallback to x86_64
+ if ( (archName != "x86_64h") || !isArch(mh, "x86_64") ) {
+ diag.warning("could not use '%s' because it does not contain required architecture %s", pathOpened.c_str(), archName.c_str());
+ return false;
+ }
+ }
+
+ // must be a filetype dyld can load
+ switch ( mh->filetype ) {
+ case MH_EXECUTE:
+ if ( ignoreMainExecutables )
+ return false;
+ break;
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ break;
+ default:
+ diag.warning("could not use '%s' because it is not a dylib, bundle, or executable", pathOpened.c_str());
+ return false;
+ }
+
+ // must be from a file - not in the dyld shared cache
+ if ( mh->flags & 0x80000000 ) {
+ diag.warning("could not use '%s' because the high bit of mach_header flags is reserved for images in dyld cache", pathOpened.c_str());
+ return false;
+ }
+
+ // validate load commands structure
+ MachOParser parser(mh);
+ if ( !parser.validLoadCommands(diag, fileLength) )
+ return false;
+
+ // must match requested platform
+ if ( parser.platform() != platform ) {
+ diag.warning("could not use '%s' because it was built for a different platform", pathOpened.c_str());
+ return false;
+ }
+
+ // cannot be a static executable
+ if ( (mh->filetype == MH_EXECUTE) && !parser.isDynamicExecutable() ) {
+ diag.warning("could not use '%s' because it is a static executable", pathOpened.c_str());
+ return false;
+ }
+
+ // validate dylib loads
+ if ( !parser.validEmbeddedPaths(diag) )
+ return false;
+
+ // validate segments
+ if ( !parser.validSegments(diag, fileLength) )
+ return false;
+
+ // validate LINKEDIT layout
+ if ( !parser.validLinkeditLayout(diag) )
+ return false;
+
+ return true;
+}
+
+
+bool MachOParser::validLoadCommands(Diagnostics& diag, size_t fileLen)
+{
+ // check load command don't exceed file length
+ if ( header()->sizeofcmds + sizeof(mach_header_64) > fileLen ) {
+ diag.warning("load commands exceed length of file");
+ return false;
+ }
+ // walk all load commands and sanity check them
+ Diagnostics walkDiag;
+ LinkEditInfo lePointers;
+ getLinkEditLoadCommands(walkDiag, lePointers);
+ if ( walkDiag.hasError() ) {
+ diag.warning("%s", walkDiag.errorMessage().c_str());
+ return false;
+ }
+
+ // check load commands fit in TEXT segment
+ __block bool overflowText = false;
+ forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ if ( header()->sizeofcmds + sizeof(mach_header_64) > segFileSize ) {
+ diag.warning("load commands exceed length of __TEXT segment");
+ overflowText = true;
+ }
+ stop = true;
+ }
+ });
+ if ( overflowText )
+ return false;
+
+ return true;
+}
+
+bool MachOParser::validEmbeddedPaths(Diagnostics& diag)
+{
+ __block int index = 1;
+ __block bool allGood = true;
+ __block bool foundInstallName = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ const dylib_command* dylibCmd;
+ const rpath_command* rpathCmd;
+ switch ( cmd->cmd ) {
+ case LC_ID_DYLIB:
+ foundInstallName = true;
+ // fall through
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) {
+ diag.warning("load command #%d name offset (%u) outside its size (%u)", index, dylibCmd->dylib.name.offset, cmd->cmdsize);
+ stop = true;
+ allGood = false;
+ }
+ else {
+ bool foundEnd = false;
+ const char* start = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+ const char* end = (char*)dylibCmd + cmd->cmdsize;
+ for (const char* s=start; s < end; ++s) {
+ if ( *s == '\0' ) {
+ foundEnd = true;
+ break;
+ }
+ }
+ if ( !foundEnd ) {
+ diag.warning("load command #%d string extends beyond end of load command", index);
+ stop = true;
+ allGood = false;
+ }
+ }
+ break;
+ case LC_RPATH:
+ rpathCmd = (rpath_command*)cmd;
+ if ( rpathCmd->path.offset > cmd->cmdsize ) {
+ diag.warning("load command #%d path offset (%u) outside its size (%u)", index, rpathCmd->path.offset, cmd->cmdsize);
+ stop = true;
+ allGood = false;
+ }
+ else {
+ bool foundEnd = false;
+ const char* start = (char*)rpathCmd + rpathCmd->path.offset;
+ const char* end = (char*)rpathCmd + cmd->cmdsize;
+ for (const char* s=start; s < end; ++s) {
+ if ( *s == '\0' ) {
+ foundEnd = true;
+ break;
+ }
+ }
+ if ( !foundEnd ) {
+ diag.warning("load command #%d string extends beyond end of load command", index);
+ stop = true;
+ allGood = false;
+ }
+ }
+ break;
+ }
+ ++index;
+ });
+
+ if ( header()->filetype == MH_DYLIB ) {
+ if ( !foundInstallName ) {
+ diag.warning("MH_DYLIB is missing LC_ID_DYLIB");
+ allGood = false;
+ }
+ }
+ else {
+ if ( foundInstallName ) {
+ diag.warning("LC_ID_DYLIB found in non-MH_DYLIB");
+ allGood = false;
+ }
+ }
+
+ return allGood;
+}
+
+bool MachOParser::validSegments(Diagnostics& diag, size_t fileLen)
+{
+ // check segment load command size
+ __block bool badSegmentLoadCommand = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command_64);
+ if ( sectionsSpace < 0 ) {
+ diag.warning("load command size too small for LC_SEGMENT_64");
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
+ diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
+ diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
+ badSegmentLoadCommand = true;
+ stop = true;
+ } else if (greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen)) {
+ diag.warning("segment load command content extends beyond end of file");
+ badSegmentLoadCommand = true;
+ stop = true;
+ } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
+ // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
+ diag.warning("segment filesize exceeds vmsize");
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command);
+ if ( sectionsSpace < 0 ) {
+ diag.warning("load command size too small for LC_SEGMENT");
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( (sectionsSpace % sizeof(section)) != 0 ) {
+ diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
+ diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
+ badSegmentLoadCommand = true;
+ stop = true;
+ } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
+ // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
+ diag.warning("segment filesize exceeds vmsize");
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ }
+ });
+ if ( badSegmentLoadCommand )
+ return false;
+
+ // check mapping permissions of segments
+ __block bool badPermissions = false;
+ __block bool badSize = false;
+ __block bool hasTEXT = false;
+ __block bool hasLINKEDIT = false;
+ forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ if ( protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+ diag.warning("__TEXT segment permissions is not 'r-x'");
+ badPermissions = true;
+ stop = true;
+ }
+ hasTEXT = true;
+ }
+ else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+ if ( protections != VM_PROT_READ ) {
+ diag.warning("__LINKEDIT segment permissions is not 'r--'");
+ badPermissions = true;
+ stop = true;
+ }
+ hasLINKEDIT = true;
+ }
+ else if ( (protections & 0xFFFFFFF8) != 0 ) {
+ diag.warning("%s segment permissions has invalid bits set", segName);
+ badPermissions = true;
+ stop = true;
+ }
+ if (greaterThanAddOrOverflow(segFileOffset, segFileSize, fileLen)) {
+ diag.warning("%s segment content extends beyond end of file", segName);
+ badSize = true;
+ stop = true;
+ }
+ if ( is64() ) {
+ if ( vmAddr+vmSize < vmAddr ) {
+ diag.warning("%s segment vm range wraps", segName);
+ badSize = true;
+ stop = true;
+ }
+ }
+ else {
+ if ( (uint32_t)(vmAddr+vmSize) < (uint32_t)(vmAddr) ) {
+ diag.warning("%s segment vm range wraps", segName);
+ badSize = true;
+ stop = true;
+ }
+ }
+ });
+ if ( badPermissions || badSize )
+ return false;
+ if ( !hasTEXT ) {
+ diag.warning("missing __TEXT segment");
+ return false;
+ }
+ if ( !hasLINKEDIT ) {
+ diag.warning("missing __LINKEDIT segment");
+ return false;
+ }
+
+ // check for overlapping segments
+ __block bool badSegments = false;
+ forEachSegment(^(const char* seg1Name, uint32_t seg1FileOffset, uint32_t seg1FileSize, uint64_t seg1vmAddr, uint64_t seg1vmSize, uint8_t seg1Protections, uint32_t seg1Index, uint64_t seg1SizeOfSections, uint8_t seg1Align, bool& stop1) {
+ uint64_t seg1vmEnd = seg1vmAddr + seg1vmSize;
+ uint32_t seg1FileEnd = seg1FileOffset + seg1FileSize;
+ forEachSegment(^(const char* seg2Name, uint32_t seg2FileOffset, uint32_t seg2FileSize, uint64_t seg2vmAddr, uint64_t seg2vmSize, uint8_t seg2Protections, uint32_t seg2Index, uint64_t seg2SizeOfSections, uint8_t seg2Align, bool& stop2) {
+ if ( seg1Index == seg2Index )
+ return;
+ uint64_t seg2vmEnd = seg2vmAddr + seg2vmSize;
+ uint32_t seg2FileEnd = seg2FileOffset + seg2FileSize;
+ if ( ((seg2vmAddr <= seg1vmAddr) && (seg2vmEnd > seg1vmAddr) && (seg1vmEnd > seg1vmAddr)) || ((seg2vmAddr >= seg1vmAddr) && (seg2vmAddr < seg1vmEnd) && (seg2vmEnd > seg2vmAddr)) ) {
+ diag.warning("segment %s vm range overlaps segment %s", seg1Name, seg2Name);
+ badSegments = true;
+ stop1 = true;
+ stop2 = true;
+ }
+ if ( ((seg2FileOffset <= seg1FileOffset) && (seg2FileEnd > seg1FileOffset) && (seg1FileEnd > seg1FileOffset)) || ((seg2FileOffset >= seg1FileOffset) && (seg2FileOffset < seg1FileEnd) && (seg2FileEnd > seg2FileOffset)) ) {
+ diag.warning("segment %s file content overlaps segment %s", seg1Name, seg2Name);
+ badSegments = true;
+ stop1 = true;
+ stop2 = true;
+ }
+ // check for out of order segments
+ if ( (seg1Index < seg2Index) && !stop1 ) {
+ if ( (seg1vmAddr > seg2vmAddr) || ((seg1FileOffset > seg2FileOffset) && (seg1FileOffset != 0) && (seg2FileOffset != 0)) ){
+ diag.warning("segment load commands out of order with respect to layout for %s and %s", seg1Name, seg2Name);
+ badSegments = true;
+ stop1 = true;
+ stop2 = true;
+ }
+ }
+ });
+ });
+ if ( badSegments )
+ return false;
+
+ // check sections are within segment
+ __block bool badSections = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
+ const section_64* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const section_64* sect=sectionsStart; (sect < sectionsEnd); ++sect) {
+ if ( (int64_t)(sect->size) < 0 ) {
+ diag.warning("section %s size too large 0x%llX", sect->sectname, sect->size);
+ badSections = true;
+ }
+ else if ( sect->addr < seg->vmaddr ) {
+ diag.warning("section %s start address 0x%llX is before containing segment's address 0x%0llX", sect->sectname, sect->addr, seg->vmaddr);
+ badSections = true;
+ }
+ else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+ diag.warning("section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+ badSections = true;
+ }
+ }
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
+ const section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+ if ( (int64_t)(sect->size) < 0 ) {
+ diag.warning("section %s size too large 0x%X", sect->sectname, sect->size);
+ badSections = true;
+ }
+ else if ( sect->addr < seg->vmaddr ) {
+ diag.warning("section %s start address 0x%X is before containing segment's address 0x%0X", sect->sectname, sect->addr, seg->vmaddr);
+ badSections = true;
+ }
+ else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+ diag.warning("section %s end address 0x%X is beyond containing segment's end address 0x%0X", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+ badSections = true;
+ }
+ }
+ }
+ });
+
+ return !badSections;
+}
+
+struct LinkEditContent
+{
+ const char* name;
+ uint32_t stdOrder;
+ uint32_t fileOffsetStart;
+ uint32_t size;
+};
+
+
+
+bool MachOParser::validLinkeditLayout(Diagnostics& diag)
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return false;
+ const bool is64Bit = is64();
+ const uint32_t pointerSize = (is64Bit ? 8 : 4);
+
+ // build vector of all blobs in LINKEDIT
+ std::vector<LinkEditContent> blobs;
+ if ( leInfo.dyldInfo != nullptr ) {
+ if ( leInfo.dyldInfo->rebase_size != 0 )
+ blobs.push_back({"rebase opcodes", 1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size});
+ if ( leInfo.dyldInfo->bind_size != 0 )
+ blobs.push_back({"bind opcodes", 2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size});
+ if ( leInfo.dyldInfo->weak_bind_size != 0 )
+ blobs.push_back({"weak bind opcodes", 3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size});
+ if ( leInfo.dyldInfo->lazy_bind_size != 0 )
+ blobs.push_back({"lazy bind opcodes", 4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size});
+ if ( leInfo.dyldInfo->export_size!= 0 )
+ blobs.push_back({"exports trie", 5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size});
+ }
+ if ( leInfo.dynSymTab != nullptr ) {
+ if ( leInfo.dynSymTab->nlocrel != 0 )
+ blobs.push_back({"local relocations", 6, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))});
+ if ( leInfo.dynSymTab->nextrel != 0 )
+ blobs.push_back({"external relocations", 11, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))});
+ if ( leInfo.dynSymTab->nindirectsyms != 0 )
+ blobs.push_back({"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4});
+ }
+ if ( leInfo.splitSegInfo != nullptr ) {
+ if ( leInfo.splitSegInfo->datasize != 0 )
+ blobs.push_back({"shared cache info", 6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize});
+ }
+ if ( leInfo.functionStarts != nullptr ) {
+ if ( leInfo.functionStarts->datasize != 0 )
+ blobs.push_back({"function starts", 7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize});
+ }
+ if ( leInfo.dataInCode != nullptr ) {
+ if ( leInfo.dataInCode->datasize != 0 )
+ blobs.push_back({"data in code", 8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize});
+ }
+ if ( leInfo.symTab != nullptr ) {
+ if ( leInfo.symTab->nsyms != 0 )
+ blobs.push_back({"symbol table", 10, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(is64Bit ? sizeof(nlist_64) : sizeof(struct nlist)))});
+ if ( leInfo.symTab->strsize != 0 )
+ blobs.push_back({"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize});
+ }
+ if ( leInfo.codeSig != nullptr ) {
+ if ( leInfo.codeSig->datasize != 0 )
+ blobs.push_back({"code signature", 21, leInfo.codeSig->dataoff, leInfo.codeSig->datasize});
+ }
+
+ // check for bad combinations
+ if ( (leInfo.dyldInfo != nullptr) && (leInfo.dyldInfo->cmd == LC_DYLD_INFO_ONLY) && (leInfo.dynSymTab != nullptr) ) {
+ if ( leInfo.dynSymTab->nlocrel != 0 ) {
+ diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations");
+ return false;
+ }
+ if ( leInfo.dynSymTab->nextrel != 0 ) {
+ diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations");
+ return false;
+ }
+ }
+ if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
+ diag.error("malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB");
+ return false;
+ }
+ if ( blobs.empty() ) {
+ diag.error("malformed mach-o misssing LINKEDIT");
+ return false;
+ }
+
+ // sort vector by file offset and error on overlaps
+ std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
+ return a.fileOffsetStart < b.fileOffsetStart;
+ });
+ uint32_t prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
+ const char* prevName = "start of LINKEDIT";
+ for (const LinkEditContent& blob : blobs) {
+ if ( blob.fileOffsetStart < prevEnd ) {
+ diag.error("LINKEDIT overlap of %s and %s", prevName, blob.name);
+ return false;
+ }
+ prevEnd = blob.fileOffsetStart + blob.size;
+ prevName = blob.name;
+ }
+ const LinkEditContent& lastBlob = blobs.back();
+ uint32_t linkeditFileEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset + leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileSize);
+ if (greaterThanAddOrOverflow(lastBlob.fileOffsetStart, lastBlob.size, linkeditFileEnd)) {
+ diag.error("LINKEDIT content '%s' extends beyond end of segment", lastBlob.name);
+ return false;
+ }
+
+ // sort vector by order and warn on non standard order or mis-alignment
+ std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
+ return a.stdOrder < b.stdOrder;
+ });
+ prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
+ prevName = "start of LINKEDIT";
+ for (const LinkEditContent& blob : blobs) {
+ if ( ((blob.fileOffsetStart & (pointerSize-1)) != 0) && (blob.stdOrder != 20) ) // ok for "symbol table strings" to be mis-aligned
+ diag.warning("mis-aligned LINKEDIT content '%s'", blob.name);
+ if ( blob.fileOffsetStart < prevEnd ) {
+ diag.warning("LINKEDIT out of order %s", blob.name);
+ }
+ prevEnd = blob.fileOffsetStart;
+ prevName = blob.name;
+ }
+
+ // Check for invalid symbol table sizes
+ if ( leInfo.symTab != nullptr ) {
+ if ( leInfo.symTab->nsyms > 0x10000000 ) {
+ diag.error("malformed mach-o image: symbol table too large");
+ return false;
+ }
+ if ( leInfo.dynSymTab != nullptr ) {
+ // validate indirect symbol table
+ if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
+ if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
+ diag.error("malformed mach-o image: indirect symbol table too large");
+ return false;
+ }
+ }
+ if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
+ diag.error("malformed mach-o image: indirect symbol table local symbol count exceeds total symbols");
+ return false;
+ }
+ if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym ) {
+ diag.error("malformed mach-o image: indirect symbol table local symbol count wraps");
+ return false;
+ }
+ if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
+ diag.error("malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols");
+ return false;
+ }
+ if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym ) {
+ diag.error("malformed mach-o image: indirect symbol table extern symbol count wraps");
+ return false;
+ }
+ if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
+ diag.error("malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols");
+ return false;
+ }
+ if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym ) {
+ diag.error("malformed mach-o image: indirect symbol table undefined symbol count wraps");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool MachOParser::isArch(const mach_header* mh, const std::string& archName)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( archName == info.name ) {
+ return ( (mh->cputype == info.cputype) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) );
+ }
+ }
+ return false;
+}
+
+
+std::string MachOParser::archName(uint32_t cputype, uint32_t cpusubtype)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
+ return info.name;
+ }
+ }
+ return "unknown";
+}
+
+uint32_t MachOParser::cpuTypeFromArchName(const std::string& archName)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( archName == info.name ) {
+ return info.cputype;
+ }
+ }
+ return 0;
+}
+
+uint32_t MachOParser::cpuSubtypeFromArchName(const std::string& archName)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( archName == info.name ) {
+ return info.cpusubtype;
+ }
+ }
+ return 0;
+}
+
+std::string MachOParser::archName() const
+{
+ return archName(header()->cputype, header()->cpusubtype);
+}
+
+std::string MachOParser::platformName(Platform platform)
+{
+ switch ( platform ) {
+ case Platform::unknown:
+ return "unknown";
+ case Platform::macOS:
+ return "macOS";
+ case Platform::iOS:
+ return "iOS";
+ case Platform::tvOS:
+ return "tvOS";
+ case Platform::watchOS:
+ return "watchOS";
+ case Platform::bridgeOS:
+ return "bridgeOS";
+ }
+ return "unknown platform";
+}
+
+std::string MachOParser::versionString(uint32_t packedVersion)
+{
+ char buff[64];
+ sprintf(buff, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
+ return buff;
+}
+
+#else
+
+bool MachOParser::isMachO(Diagnostics& diag, const void* fileContent, size_t mappedLength)
+{
+ // sanity check length
+ if ( mappedLength < 4096 ) {
+ diag.error("file too short");
+ return false;
+ }
+
+ // must start with mach-o magic value
+ const mach_header* mh = (const mach_header*)fileContent;
+#if __LP64__
+ const uint32_t requiredMagic = MH_MAGIC_64;
+#else
+ const uint32_t requiredMagic = MH_MAGIC;
+#endif
+ if ( mh->magic != requiredMagic ) {
+ diag.error("not a mach-o file");
+ return false;
+ }
+
+#if __x86_64__
+ const uint32_t requiredCPU = CPU_TYPE_X86_64;
+#elif __i386__
+ const uint32_t requiredCPU = CPU_TYPE_I386;
+#elif __arm__
+ const uint32_t requiredCPU = CPU_TYPE_ARM;
+#elif __arm64__
+ const uint32_t requiredCPU = CPU_TYPE_ARM64;
+#else
+ #error unsupported architecture
+#endif
+ if ( mh->cputype != requiredCPU ) {
+ diag.error("wrong cpu type");
+ return false;
+ }
+
+ return true;
+}
+
+bool MachOParser::wellFormedMachHeaderAndLoadCommands(const mach_header* mh)
+{
+ const load_command* startCmds = nullptr;
+ 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;
+ }
+ cmd = nextCmd;
+ }
+ return true;
+}
+
+#endif
+
+Platform MachOParser::currentPlatform()
+{
+#if TARGET_OS_BRIDGE
+ return Platform::bridgeOS;
+#elif TARGET_OS_WATCH
+ return Platform::watchOS;
+#elif TARGET_OS_TV
+ return Platform::tvOS;
+#elif TARGET_OS_IOS
+ return Platform::iOS;
+#elif TARGET_OS_MAC
+ return Platform::macOS;
+#else
+ #error unknown platform
+#endif
+}
+
+
+bool MachOParser::valid(Diagnostics& diag)
+{
+#if DYLD_IN_PROCESS
+ // only images loaded by dyld to be parsed
+ const mach_header* inImage = dyld3::dyld_image_header_containing_address(header());
+ if ( inImage != header() ) {
+ diag.error("only dyld loaded images can be parsed by MachOParser");
+ return false;
+ }
+#else
+
+#endif
+ return true;
+}
+
+
+void MachOParser::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
+{
+ bool stop = false;
+ const load_command* startCmds = nullptr;
+ if ( header()->magic == MH_MAGIC_64 )
+ startCmds = (load_command*)((char *)header() + sizeof(mach_header_64));
+ else if ( header()->magic == MH_MAGIC )
+ startCmds = (load_command*)((char *)header() + sizeof(mach_header));
+ else {
+ diag.error("file does not start with MH_MAGIC[_64]");
+ return; // not a mach-o file, or wrong endianness
+ }
+ const load_command* const cmdsEnd = (load_command*)((char*)startCmds + header()->sizeofcmds);
+ const load_command* cmd = startCmds;
+ for(uint32_t i = 0; i < header()->ncmds; ++i) {
+ const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+ if ( cmd->cmdsize < 8 ) {
+ diag.error("malformed load command #%d, size too small %d", i, cmd->cmdsize);
+ return;
+ }
+ if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+ diag.error("malformed load command #%d, size too large 0x%X", i, cmd->cmdsize);
+ return;
+ }
+ callback(cmd, stop);
+ if ( stop )
+ return;
+ cmd = nextCmd;
+ }
+}
+
+UUID MachOParser::uuid() const
+{
+ uuid_t uuid;
+ getUuid(uuid);
+ return uuid;
+}
+
+bool MachOParser::getUuid(uuid_t uuid) const
+{
+ Diagnostics diag;
+ __block bool found = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_UUID ) {
+ const uuid_command* uc = (const uuid_command*)cmd;
+ memcpy(uuid, uc->uuid, sizeof(uuid_t));
+ found = true;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ if ( !found )
+ bzero(uuid, sizeof(uuid_t));
+ return found;
+}
+
+uint64_t MachOParser::preferredLoadAddress() const
+{
+ __block uint64_t result = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ result = vmAddr;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool MachOParser::getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const
+{
+ Diagnostics diag;
+ __block bool found = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ const version_min_command* versCmd;
+ switch ( cmd->cmd ) {
+ case LC_VERSION_MIN_IPHONEOS:
+ versCmd = (version_min_command*)cmd;
+ *platform = Platform::iOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ found = true;
+ stop = true;
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ versCmd = (version_min_command*)cmd;
+ *platform = Platform::macOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ found = true;
+ stop = true;
+ break;
+ case LC_VERSION_MIN_TVOS:
+ versCmd = (version_min_command*)cmd;
+ *platform = Platform::tvOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ found = true;
+ stop = true;
+ break;
+ case LC_VERSION_MIN_WATCHOS:
+ versCmd = (version_min_command*)cmd;
+ *platform = Platform::watchOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ found = true;
+ stop = true;
+ break;
+ case LC_BUILD_VERSION: {
+ const build_version_command* buildCmd = (build_version_command *)cmd;
+ *minOS = buildCmd->minos;
+ *sdk = buildCmd->sdk;
+
+ switch(buildCmd->platform) {
+ /* Known values for the platform field above. */
+ case PLATFORM_MACOS:
+ *platform = Platform::macOS;
+ break;
+ case PLATFORM_IOS:
+ *platform = Platform::iOS;
+ break;
+ case PLATFORM_TVOS:
+ *platform = Platform::tvOS;
+ break;
+ case PLATFORM_WATCHOS:
+ *platform = Platform::watchOS;
+ break;
+ case PLATFORM_BRIDGEOS:
+ *platform = Platform::bridgeOS;
+ break;
+ }
+ found = true;
+ stop = true;
+ } break;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return found;
+}
+
+
+bool MachOParser::isSimulatorBinary() const
+{
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+ switch ( header()->cputype ) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ return (platform != Platform::macOS);
+ }
+ break;
+ }
+ return false;
+}
+
+
+bool MachOParser::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
+{
+ Diagnostics diag;
+ __block bool found = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_ID_DYLIB ) {
+ const dylib_command* dylibCmd = (dylib_command*)cmd;
+ *compatVersion = dylibCmd->dylib.compatibility_version;
+ *currentVersion = dylibCmd->dylib.current_version;
+ *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+ found = true;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return found;
+}
+
+const char* MachOParser::installName() const
+{
+ assert(header()->filetype == MH_DYLIB);
+ const char* result;
+ uint32_t ignoreVersion;
+ assert(getDylibInstallName(&result, &ignoreVersion, &ignoreVersion));
+ return result;
+}
+
+
+uint32_t MachOParser::dependentDylibCount() const
+{
+ __block uint32_t count = 0;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ ++count;
+ });
+ return count;
+}
+
+const char* MachOParser::dependentDylibLoadPath(uint32_t depIndex) const
+{
+ __block const char* foundLoadPath = nullptr;
+ __block uint32_t curDepIndex = 0;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( curDepIndex == depIndex ) {
+ foundLoadPath = loadPath;
+ stop = true;
+ }
+ ++curDepIndex;
+ });
+ return foundLoadPath;
+}
+
+
+void MachOParser::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ switch ( cmd->cmd ) {
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB: {
+ const dylib_command* dylibCmd = (dylib_command*)cmd;
+ assert(dylibCmd->dylib.name.offset < cmd->cmdsize);
+ const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+ callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
+ dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
+ }
+ break;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_RPATH ) {
+ const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
+ callback(rpath, stop);
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+/*
+ struct LayoutInfo {
+#if DYLD_IN_PROCESS
+ uintptr_t slide;
+ uintptr_t textUnslidVMAddr;
+ uintptr_t linkeditUnslidVMAddr;
+ uint32_t linkeditFileOffset;
+#else
+ uint32_t segmentCount;
+ uint32_t linkeditSegIndex;
+ struct {
+ uint64_t mappingOffset;
+ uint64_t fileOffset;
+ uint64_t segUnslidAddress;
+ uint64_t segSize;
+ } segments[16];
+#endif
+ };
+*/
+
+#if !DYLD_IN_PROCESS
+const uint8_t* MachOParser::getContentForVMAddr(const LayoutInfo& info, uint64_t addr) const
+{
+ for (uint32_t i=0; i < info.segmentCount; ++i) {
+ if ( (addr >= info.segments[i].segUnslidAddress) && (addr < (info.segments[i].segUnslidAddress+info.segments[i].segSize)) )
+ return (uint8_t*)header() + info.segments[i].mappingOffset + (addr - info.segments[i].segUnslidAddress);
+ }
+ // value is outside this image. could be pointer into another image
+ if ( inDyldCache() ) {
+ return (uint8_t*)header() + info.segments[0].mappingOffset + (addr - info.segments[0].segUnslidAddress);
+ }
+ assert(0 && "address not found in segment");
+ return nullptr;
+}
+#endif
+
+const uint8_t* MachOParser::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const
+{
+#if DYLD_IN_PROCESS
+ uint32_t offsetInLinkedit = fileOffset - info.linkeditFileOffset;
+ uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide;
+ return (uint8_t*)(linkeditStartAddr + offsetInLinkedit);
+#else
+ uint32_t offsetInLinkedit = fileOffset - (uint32_t)(info.segments[info.linkeditSegIndex].fileOffset);
+ const uint8_t* linkeditStart = (uint8_t*)header() + info.segments[info.linkeditSegIndex].mappingOffset;
+ return linkeditStart + offsetInLinkedit;
+#endif
+}
+
+
+void MachOParser::getLayoutInfo(LayoutInfo& result) const
+{
+#if DYLD_IN_PROCESS
+ // image loaded by dyld, just record the addr and file offset of TEXT and LINKEDIT segments
+ result.slide = getSlide();
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ result.textUnslidVMAddr = (uintptr_t)vmAddr;
+ }
+ else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+ result.linkeditUnslidVMAddr = (uintptr_t)vmAddr;
+ result.linkeditFileOffset = fileOffset;
+ }
+ });
+#else
+ bool inCache = inDyldCache();
+ bool intel32 = (header()->cputype == CPU_TYPE_I386);
+ result.segmentCount = 0;
+ result.linkeditSegIndex = 0xFFFFFFFF;
+ __block uint64_t textSegAddr = 0;
+ __block uint64_t textSegFileOffset = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ auto& segInfo = result.segments[result.segmentCount];
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ textSegAddr = vmAddr;
+ textSegFileOffset = fileOffset;
+ }
+ __block bool textRelocsAllowed = false;
+ if ( intel32 ) {
+ forEachSection(^(const char* curSegName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+ uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
+ if ( strcmp(curSegName, segName) == 0 ) {
+ if ( sectFlags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) ) {
+ textRelocsAllowed = true;
+ sectStop = true;
+ }
+ }
+ });
+ }
+ if ( inCache ) {
+ if ( inRawCache() ) {
+ // whole cache file mapped somewhere (padding not expanded)
+ // vmaddrs are useless. only file offset make sense
+ segInfo.mappingOffset = fileOffset - textSegFileOffset;
+ }
+ else {
+ // cache file was loaded by dyld into shared region
+ // vmaddrs of segments are correct except for ASLR slide
+ segInfo.mappingOffset = vmAddr - textSegAddr;
+ }
+ }
+ else {
+ // individual mach-o file mapped in one region, so mappingOffset == fileOffset
+ segInfo.mappingOffset = fileOffset;
+ }
+ segInfo.fileOffset = fileOffset;
+ segInfo.fileSize = fileSize;
+ segInfo.segUnslidAddress = vmAddr;
+ segInfo.segSize = vmSize;
+ segInfo.writable = ((protections & VM_PROT_WRITE) == VM_PROT_WRITE);
+ segInfo.executable = ((protections & VM_PROT_EXECUTE) == VM_PROT_EXECUTE);
+ segInfo.textRelocsAllowed = textRelocsAllowed;
+ if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+ result.linkeditSegIndex = result.segmentCount;
+ }
+ ++result.segmentCount;
+ if ( result.segmentCount > 127 )
+ stop = true;
+ });
+#endif
+}
+
+
+void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags,
+ const void* content, size_t size, bool illegalSectionSize, bool& stop)) const
+{
+ forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
+ const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+ callback(segName, sectionName, flags, content, (size_t)size, illegalSectionSize, stop);
+ });
+}
+
+void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
+ const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2,
+ bool illegalSectionSize, bool& stop)) const
+{
+ Diagnostics diag;
+ //fprintf(stderr, "forEachSection() mh=%p\n", header());
+ LayoutInfo layout;
+ getLayoutInfo(layout);
+ forEachSection(^(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+ uint64_t sectAddr, uint64_t sectSize, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+ #if DYLD_IN_PROCESS
+ const uint8_t* segContentStart = (uint8_t*)(segVMAddr + layout.slide);
+ #else
+ const uint8_t* segContentStart = (uint8_t*)header() + layout.segments[segIndex].mappingOffset;
+ #endif
+ const void* contentAddr = segContentStart + (sectAddr - segVMAddr);
+ callback(segName, sectionName, sectFlags, sectAddr, contentAddr, sectSize, alignP2, reserved1, reserved2, illegalSectionSize, stop);
+ });
+
+}
+
+// this iterator just walks the segment/section array. It does interpret addresses
+void MachOParser::forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+ uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const
+{
+ Diagnostics diag;
+ //fprintf(stderr, "forEachSection() mh=%p\n", header());
+ __block uint32_t segIndex = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
+ const section_64* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+ const char* sectName = sect->sectname;
+ char sectNameCopy[20];
+ if ( sectName[15] != '\0' ) {
+ strlcpy(sectNameCopy, sectName, 17);
+ sectName = sectNameCopy;
+ }
+ bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
+ callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
+ }
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
+ const section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+ const char* sectName = sect->sectname;
+ char sectNameCopy[20];
+ if ( sectName[15] != '\0' ) {
+ strlcpy(sectNameCopy, sectName, 17);
+ sectName = sectNameCopy;
+ }
+ bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
+ callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
+ }
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ const bool is64Bit = is64();
+ if ( leInfo.symTab != nullptr ) {
+ uint32_t globalsStartIndex = 0;
+ uint32_t globalsCount = leInfo.symTab->nsyms;
+ if ( leInfo.dynSymTab != nullptr ) {
+ globalsStartIndex = leInfo.dynSymTab->iextdefsym;
+ globalsCount = leInfo.dynSymTab->nextdefsym;
+ }
+ uint32_t maxStringOffset = leInfo.symTab->strsize;
+ const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+ const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+ const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+ bool stop = false;
+ for (uint32_t i=0; (i < globalsCount) && !stop; ++i) {
+ if ( is64Bit ) {
+ const struct nlist_64& sym = symbols64[globalsStartIndex+i];
+ if ( sym.n_un.n_strx > maxStringOffset )
+ continue;
+ if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+ callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+ }
+ else {
+ const struct nlist& sym = symbols[globalsStartIndex+i];
+ if ( sym.n_un.n_strx > maxStringOffset )
+ continue;
+ if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+ callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+ }
+ }
+ }
+}
+
+void MachOParser::forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ const bool is64Bit = is64();
+ if ( leInfo.symTab != nullptr ) {
+ uint32_t localsStartIndex = 0;
+ uint32_t localsCount = leInfo.symTab->nsyms;
+ if ( leInfo.dynSymTab != nullptr ) {
+ localsStartIndex = leInfo.dynSymTab->ilocalsym;
+ localsCount = leInfo.dynSymTab->nlocalsym;
+ }
+ uint32_t maxStringOffset = leInfo.symTab->strsize;
+ const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+ const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+ const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+ bool stop = false;
+ for (uint32_t i=0; (i < localsCount) && !stop; ++i) {
+ if ( is64Bit ) {
+ const struct nlist_64& sym = symbols64[localsStartIndex+i];
+ if ( sym.n_un.n_strx > maxStringOffset )
+ continue;
+ if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+ callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+ }
+ else {
+ const struct nlist& sym = symbols[localsStartIndex+i];
+ if ( sym.n_un.n_strx > maxStringOffset )
+ continue;
+ if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+ callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+ }
+ }
+ }
+}
+
+
+bool MachOParser::findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder findDependent) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return false;
+ if ( leInfo.dyldInfo != nullptr ) {
+ const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
+ const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size;
+ const uint8_t* node = trieWalk(diag, trieStart, trieEnd, symbolName);
+ if ( node == nullptr ) {
+ // symbol not exported from this image. Seach any re-exported dylibs
+ __block unsigned depIndex = 0;
+ __block bool foundInReExportedDylib = false;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( isReExport && findDependent ) {
+ const mach_header* depMH;
+ void* depExtra;
+ if ( findDependent(depIndex, loadPath, extra, &depMH, &depExtra) ) {
+ bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
+ MachOParser dep(depMH, depInRawCache);
+ if ( dep.findExportedSymbol(diag, symbolName, depExtra, foundInfo, findDependent) ) {
+ stop = true;
+ foundInReExportedDylib = true;
+ }
+ }
+ else {
+ fprintf(stderr, "could not find re-exported dylib %s\n", loadPath);
+ }
+ }
+ ++depIndex;
+ });
+ return foundInReExportedDylib;
+ }
+ const uint8_t* p = node;
+ const uint64_t flags = read_uleb128(diag, p, trieEnd);
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ if ( !findDependent )
+ return false;
+ // re-export from another dylib, lookup there
+ const uint64_t ordinal = read_uleb128(diag, p, trieEnd);
+ const char* importedName = (char*)p;
+ if ( importedName[0] == '\0' )
+ importedName = symbolName;
+ assert(ordinal >= 1);
+ if (ordinal > dependentDylibCount()) {
+ diag.error("ordinal %lld out of range for %s", ordinal, symbolName);
+ return false;
+ }
+ uint32_t depIndex = (uint32_t)(ordinal-1);
+ const mach_header* depMH;
+ void* depExtra;
+ if ( findDependent(depIndex, dependentDylibLoadPath(depIndex), extra, &depMH, &depExtra) ) {
+ bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
+ MachOParser depParser(depMH, depInRawCache);
+ return depParser.findExportedSymbol(diag, importedName, depExtra, foundInfo, findDependent);
+ }
+ else {
+ diag.error("dependent dylib %lld not found for re-exported symbol %s", ordinal, symbolName);
+ return false;
+ }
+ }
+ foundInfo.kind = FoundSymbol::Kind::headerOffset;
+ foundInfo.isThreadLocal = false;
+ foundInfo.foundInDylib = header();
+ foundInfo.foundExtra = extra;
+ foundInfo.value = read_uleb128(diag, p, trieEnd);
+ foundInfo.resolverFuncOffset = 0;
+ foundInfo.foundSymbolName = symbolName;
+ if ( diag.hasError() )
+ return false;
+ switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+ case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+ foundInfo.kind = FoundSymbol::Kind::headerOffset;
+ foundInfo.resolverFuncOffset = (uint32_t)read_uleb128(diag, p, trieEnd);
+ }
+ else {
+ foundInfo.kind = FoundSymbol::Kind::headerOffset;
+ }
+ break;
+ case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+ foundInfo.isThreadLocal = true;
+ break;
+ case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+ foundInfo.kind = FoundSymbol::Kind::absolute;
+ break;
+ default:
+ diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-trieStart));
+ return false;
+ }
+ return true;
+ }
+ else {
+ // this is an old binary (before macOS 10.6), scan the symbol table
+ foundInfo.foundInDylib = nullptr;
+ uint64_t baseAddress = preferredLoadAddress();
+ forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+ if ( strcmp(aSymbolName, symbolName) == 0 ) {
+ foundInfo.kind = FoundSymbol::Kind::headerOffset;
+ foundInfo.isThreadLocal = false;
+ foundInfo.foundInDylib = header();
+ foundInfo.foundExtra = extra;
+ foundInfo.value = n_value - baseAddress;
+ foundInfo.resolverFuncOffset = 0;
+ foundInfo.foundSymbolName = symbolName;
+ stop = true;
+ }
+ });
+ return (foundInfo.foundInDylib != nullptr);
+ }
+}
+
+
+void MachOParser::getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const
+{
+ result.dyldInfo = nullptr;
+ result.symTab = nullptr;
+ result.dynSymTab = nullptr;
+ result.splitSegInfo = nullptr;
+ result.functionStarts = nullptr;
+ result.dataInCode = nullptr;
+ result.codeSig = nullptr;
+ __block bool hasUUID = false;
+ __block bool hasVersion = false;
+ __block bool hasEncrypt = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ switch ( cmd->cmd ) {
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ if ( cmd->cmdsize != sizeof(dyld_info_command) )
+ diag.error("LC_DYLD_INFO load command size wrong");
+ else if ( result.dyldInfo != nullptr )
+ diag.error("multiple LC_DYLD_INFO load commands");
+ result.dyldInfo = (dyld_info_command*)cmd;
+ break;
+ case LC_SYMTAB:
+ if ( cmd->cmdsize != sizeof(symtab_command) )
+ diag.error("LC_SYMTAB load command size wrong");
+ else if ( result.symTab != nullptr )
+ diag.error("multiple LC_SYMTAB load commands");
+ result.symTab = (symtab_command*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ if ( cmd->cmdsize != sizeof(dysymtab_command) )
+ diag.error("LC_DYSYMTAB load command size wrong");
+ else if ( result.dynSymTab != nullptr )
+ diag.error("multiple LC_DYSYMTAB load commands");
+ result.dynSymTab = (dysymtab_command*)cmd;
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ diag.error("LC_SEGMENT_SPLIT_INFO load command size wrong");
+ else if ( result.splitSegInfo != nullptr )
+ diag.error("multiple LC_SEGMENT_SPLIT_INFO load commands");
+ result.splitSegInfo = (linkedit_data_command*)cmd;
+ break;
+ case LC_FUNCTION_STARTS:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ diag.error("LC_FUNCTION_STARTS load command size wrong");
+ else if ( result.functionStarts != nullptr )
+ diag.error("multiple LC_FUNCTION_STARTS load commands");
+ result.functionStarts = (linkedit_data_command*)cmd;
+ break;
+ case LC_DATA_IN_CODE:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ diag.error("LC_DATA_IN_CODE load command size wrong");
+ else if ( result.dataInCode != nullptr )
+ diag.error("multiple LC_DATA_IN_CODE load commands");
+ result.dataInCode = (linkedit_data_command*)cmd;
+ break;
+ case LC_CODE_SIGNATURE:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ diag.error("LC_CODE_SIGNATURE load command size wrong");
+ else if ( result.codeSig != nullptr )
+ diag.error("multiple LC_CODE_SIGNATURE load commands");
+ result.codeSig = (linkedit_data_command*)cmd;
+ break;
+ case LC_UUID:
+ if ( cmd->cmdsize != sizeof(uuid_command) )
+ diag.error("LC_UUID load command size wrong");
+ else if ( hasUUID )
+ diag.error("multiple LC_UUID load commands");
+ hasUUID = true;
+ break;
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
+ if ( cmd->cmdsize != sizeof(version_min_command) )
+ diag.error("LC_VERSION_* load command size wrong");
+ else if ( hasVersion )
+ diag.error("multiple LC_VERSION_MIN_* load commands");
+ hasVersion = true;
+ break;
+ case LC_BUILD_VERSION:
+ if ( cmd->cmdsize != (sizeof(build_version_command) + ((build_version_command*)cmd)->ntools * sizeof(build_tool_version)) )
+ diag.error("LC_BUILD_VERSION load command size wrong");
+ else if ( hasVersion )
+ diag.error("multiple LC_BUILD_VERSION load commands");
+ hasVersion = true;
+ break;
+ case LC_ENCRYPTION_INFO:
+ if ( cmd->cmdsize != sizeof(encryption_info_command) )
+ diag.error("LC_ENCRYPTION_INFO load command size wrong");
+ else if ( hasEncrypt )
+ diag.error("multiple LC_ENCRYPTION_INFO load commands");
+ else if ( is64() )
+ diag.error("LC_ENCRYPTION_INFO found in 64-bit mach-o");
+ hasEncrypt = true;
+ break;
+ case LC_ENCRYPTION_INFO_64:
+ if ( cmd->cmdsize != sizeof(encryption_info_command_64) )
+ diag.error("LC_ENCRYPTION_INFO_64 load command size wrong");
+ else if ( hasEncrypt )
+ diag.error("multiple LC_ENCRYPTION_INFO_64 load commands");
+ else if ( !is64() )
+ diag.error("LC_ENCRYPTION_INFO_64 found in 32-bit mach-o");
+ hasEncrypt = true;
+ break;
+ }
+ });
+ if ( diag.noError() && (result.dynSymTab != nullptr) && (result.symTab == nullptr) )
+ diag.error("LC_DYSYMTAB but no LC_SYMTAB load command");
+
+}
+
+void MachOParser::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const
+{
+ getLinkEditLoadCommands(diag, result);
+ if ( diag.noError() )
+ getLayoutInfo(result.layout);
+}
+
+void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ callback(seg->segname, (uint32_t)seg->fileoff, (uint32_t)seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ callback(seg->segname, seg->fileoff, seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+const uint8_t* MachOParser::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol)
+{
+ uint32_t visitedNodeOffsets[128];
+ int visitedNodeOffsetCount = 0;
+ visitedNodeOffsets[visitedNodeOffsetCount++] = 0;
+ const uint8_t* p = start;
+ while ( p < end ) {
+ uint64_t terminalSize = *p++;
+ if ( terminalSize > 127 ) {
+ // except for re-export-with-rename, all terminal sizes fit in one byte
+ --p;
+ terminalSize = read_uleb128(diag, p, end);
+ if ( diag.hasError() )
+ return nullptr;
+ }
+ if ( (*symbol == '\0') && (terminalSize != 0) ) {
+ return p;
+ }
+ const uint8_t* children = p + terminalSize;
+ if ( children > end ) {
+ diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize);
+ return nullptr;
+ }
+ uint8_t childrenRemaining = *children++;
+ p = children;
+ uint64_t nodeOffset = 0;
+ for (; childrenRemaining > 0; --childrenRemaining) {
+ const char* ss = symbol;
+ 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 ) {
+ diag.error("malformed trie node, child node extends past end of trie\n");
+ return nullptr;
+ }
+ }
+ else {
+ // the symbol so far matches this edge (child)
+ // so advance to the child's node
+ ++p;
+ nodeOffset = read_uleb128(diag, p, end);
+ if ( diag.hasError() )
+ return nullptr;
+ if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) {
+ diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
+ return nullptr;
+ }
+ symbol = ss;
+ break;
+ }
+ }
+ if ( nodeOffset != 0 ) {
+ if ( nodeOffset > (end-start) ) {
+ diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
+ return nullptr;
+ }
+ for (int i=0; i < visitedNodeOffsetCount; ++i) {
+ if ( visitedNodeOffsets[i] == nodeOffset ) {
+ diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset);
+ return nullptr;
+ }
+ }
+ visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset;
+ if ( visitedNodeOffsetCount >= 128 ) {
+ diag.error("malformed trie too deep\n");
+ return nullptr;
+ }
+ p = &start[nodeOffset];
+ }
+ else
+ p = end;
+ }
+ return nullptr;
+}
+
+
+uint64_t MachOParser::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if ( p == end ) {
+ diag.error("malformed uleb128");
+ break;
+ }
+ uint64_t slice = *p & 0x7f;
+
+ if ( bit > 63 ) {
+ diag.error("uleb128 too big for uint64");
+ break;
+ }
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return result;
+}
+
+
+int64_t MachOParser::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
+{
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte = 0;
+ do {
+ if ( p == end ) {
+ diag.error("malformed sleb128");
+ break;
+ }
+ 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;
+}
+
+bool MachOParser::is64() const
+{
+#if DYLD_IN_PROCESS
+ return (sizeof(void*) == 8);
+#else
+ return (header()->magic == MH_MAGIC_64);
+#endif
+}
+
+
+
+
+bool MachOParser::findClosestSymbol(uint64_t targetUnslidAddress, const char** symbolName, uint64_t* symbolUnslidAddr) const
+{
+ Diagnostics diag;
+ __block uint64_t closestNValueSoFar = 0;
+ __block const char* closestNameSoFar = nullptr;
+ forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+ if ( n_value <= targetUnslidAddress ) {
+ if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
+ closestNValueSoFar = n_value;
+ closestNameSoFar = aSymbolName;
+ }
+ }
+ });
+ forEachLocalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+ if ( n_value <= targetUnslidAddress ) {
+ if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
+ closestNValueSoFar = n_value;
+ closestNameSoFar = aSymbolName;
+ }
+ }
+ });
+ if ( closestNameSoFar == nullptr ) {
+ return false;
+ }
+
+ *symbolName = closestNameSoFar;
+ *symbolUnslidAddr = closestNValueSoFar;
+ return true;
+}
+
+
+#if DYLD_IN_PROCESS
+
+bool MachOParser::findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const
+{
+ uint64_t slide = getSlide();
+ uint64_t symbolUnslidAddr;
+ if ( findClosestSymbol((uint64_t)addr - slide, symbolName, &symbolUnslidAddr) ) {
+ *symbolAddress = (const void*)(long)(symbolUnslidAddr + slide);
+ return true;
+ }
+ return false;
+}
+
+intptr_t MachOParser::getSlide() const
+{
+ Diagnostics diag;
+ __block intptr_t slide = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+#if __LP64__
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+ slide = ((uint64_t)header()) - seg->vmaddr;
+ stop = true;
+ }
+ }
+#else
+ if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+ slide = ((uint32_t)header()) - seg->vmaddr;
+ stop = true;
+ }
+ }
+#endif
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return slide;
+}
+
+// this is only used by dlsym() at runtime. All other binding is done when the closure is built.
+bool MachOParser::hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const
+{
+ typedef void* (*ResolverFunc)(void);
+ ResolverFunc resolver;
+ Diagnostics diag;
+ FoundSymbol foundInfo;
+ if ( findExportedSymbol(diag, symbolName, (void*)header(), foundInfo, finder) ) {
+ switch ( foundInfo.kind ) {
+ case FoundSymbol::Kind::headerOffset:
+ *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value;
+ break;
+ case FoundSymbol::Kind::absolute:
+ *result = (void*)(long)foundInfo.value;
+ break;
+ case FoundSymbol::Kind::resolverOffset:
+ // foundInfo.value contains "stub".
+ // in dlsym() we want to call resolver function to get final function address
+ resolver = (ResolverFunc)((uint8_t*)foundInfo.foundInDylib + foundInfo.resolverFuncOffset);
+ *result = (*resolver)();
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+const char* MachOParser::segmentName(uint32_t targetSegIndex) const
+{
+ __block const char* result = nullptr;
+ __block uint32_t segIndex = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( segIndex == targetSegIndex ) {
+ result = segName;
+ stop = true;
+ }
+ ++segIndex;
+ });
+ return result;
+}
+
+#else
+
+
+bool MachOParser::uses16KPages() const
+{
+ return (header()->cputype == CPU_TYPE_ARM64);
+}
+
+
+bool MachOParser::isEncrypted() const
+{
+ __block bool result = false;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
+ result = true;
+ stop = true;
+ }
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
+ result = true;
+ stop = true;
+ }
+ }
+ else if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+ const encryption_info_command* encCmd = (encryption_info_command*)cmd;
+ if ( encCmd->cryptid != 0 ) {
+ result = true;
+ stop = true;
+ }
+ }
+ });
+ return result;
+}
+
+bool MachOParser::hasWeakDefs() const
+{
+ return (header()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK));
+}
+
+bool MachOParser::hasObjC() const
+{
+ __block bool result = false;
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+ if ( (strncmp(sectionName, "__objc_imageinfo", 16) == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+ result = true;
+ stop = true;
+ }
+ if ( (header()->cputype == CPU_TYPE_I386) && (strcmp(sectionName, "__image_info") == 0) && (strcmp(segmentName, "__OBJC") == 0) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool MachOParser::hasPlusLoadMethod(Diagnostics& diag) const
+{
+#if 1
+ __block bool result = false;
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+ if ( ( (flags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+ if (illegalSectionSize) {
+ diag.error("cstring section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+ return;
+ }
+ const char* s = (char*)content;
+ const char* end = s + size;
+ while ( s < end ) {
+ if ( strcmp(s, "load") == 0 ) {
+ result = true;
+ stop = true;
+ return;
+ }
+ while (*s != '\0' )
+ ++s;
+ ++s;
+ }
+ }
+ });
+ return result;
+#else
+ LayoutInfo layout;
+ getLayoutInfo(layout);
+
+ __block bool hasSwift = false;
+ __block const void* classList = nullptr;
+ __block size_t classListSize = 0;
+ __block const void* objcData = nullptr;
+ __block size_t objcDataSize = 0;
+ __block const void* objcConstData = nullptr;
+ __block size_t objcConstDataSize = 0;
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool& stop) {
+ if ( (strcmp(sectionName, "__objc_classlist") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+ classList = content;
+ classListSize = size;
+ }
+ if ( (strcmp(sectionName, "__objc_imageinfo") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+ const uint32_t* info = (uint32_t*)content;
+ uint8_t swiftVersion = (info[1] >> 8) & 0xFF;
+ if ( swiftVersion != 0 )
+ hasSwift = true;
+ }
+ });
+ if ( classList == nullptr )
+ return false;
+ // FIXME: might be objc and swift intermixed
+ if ( hasSwift )
+ return true;
+ const bool p64 = is64();
+ const uint32_t pointerSize = (p64 ? 8 : 4);
+ const uint64_t* classArray64 = (uint64_t*)classList;
+ const uint32_t* classArray32 = (uint32_t*)classList;
+ const uint32_t classListCount = (uint32_t)(classListSize/pointerSize);
+ for (uint32_t i=0; i < classListCount; ++i) {
+ if ( p64 ) {
+ uint64_t classObjAddr = classArray64[i];
+ const uint64_t* classObjContent = (uint64_t*)getContentForVMAddr(layout, classObjAddr);
+ uint64_t classROAddr = classObjContent[4];
+ uint64_t metaClassObjAddr = classObjContent[0];
+ const uint64_t* metaClassObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassObjAddr);
+ uint64_t metaClassROObjAddr = metaClassObjContent[4];
+ const uint64_t* metaClassROObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassROObjAddr);
+ uint64_t metaClassMethodListAddr = metaClassROObjContent[4];
+ if ( metaClassMethodListAddr != 0 ) {
+ const uint64_t* metaClassMethodListContent = (uint64_t*)getContentForVMAddr(layout, metaClassMethodListAddr);
+ const uint32_t methodListCount = ((uint32_t*)metaClassMethodListContent)[1];
+ for (uint32_t m=0; m < methodListCount; ++m) {
+ uint64_t methodNameAddr = metaClassMethodListContent[m*3+1];
+ const char* methodNameContent = (char*)getContentForVMAddr(layout, methodNameAddr);
+ if ( strcmp(methodNameContent, "load") == 0 ) {
+ return true;
+ }
+ }
+ }
+ }
+ else {
+
+ }
+ }
+
+ return false;
+#endif
+}
+
+bool MachOParser::getCDHash(uint8_t cdHash[20])
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() || (leInfo.codeSig == nullptr) )
+ return false;
+
+ return cdHashOfCodeSignature(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize, cdHash);
+ }
+
+bool MachOParser::usesLibraryValidation() const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() || (leInfo.codeSig == nullptr) )
+ return false;
+
+ const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize);
+ if ( cd == nullptr )
+ return false;
+
+ // check for CS_REQUIRE_LV in CS_CodeDirectory.flags
+ return (htonl(cd->flags) & CS_REQUIRE_LV);
+ }
+
+
+bool MachOParser::isRestricted() const
+{
+ __block bool result = false;
+ forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+ if ( (strcmp(segName, "__RESTRICT") == 0) && (strcmp(sectionName, "__restrict") == 0) ) {
+ result = true;
+ stop = true;
+ }
+
+ });
+ return result;
+}
+
+bool MachOParser::hasCodeSignature(uint32_t& fileOffset, uint32_t& size)
+{
+ fileOffset = 0;
+ size = 0;
+
+ // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+ Platform platform;
+ uint32_t minOS;
+ uint32_t sdk;
+ if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+ // if have LC_VERSION_MIN_MACOSX and it says SDK < 10.9, so ignore code signature
+ if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
+ return false;
+ }
+ else {
+ switch ( header()->cputype ) {
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ // old binary with no LC_VERSION_*, assume intel binaries are old macOS binaries (ignore code signature)
+ return false;
+ }
+ }
+
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_CODE_SIGNATURE ) {
+ const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+ fileOffset = sigCmd->dataoff;
+ size = sigCmd->datasize;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return (fileOffset != 0);
+}
+
+bool MachOParser::getEntry(uint32_t& offset, bool& usesCRT)
+{
+ Diagnostics diag;
+ offset = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_MAIN ) {
+ entry_point_command* mainCmd = (entry_point_command*)cmd;
+ usesCRT = false;
+ offset = (uint32_t)mainCmd->entryoff;
+ stop = true;
+ }
+ else if ( cmd->cmd == LC_UNIXTHREAD ) {
+ stop = true;
+ usesCRT = true;
+ const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
+ const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+ uint64_t startAddress = 0;
+ switch ( header()->cputype ) {
+ case CPU_TYPE_I386:
+ startAddress = regs32[10]; // i386_thread_state_t.eip
+ break;
+ case CPU_TYPE_X86_64:
+ startAddress = regs64[16]; // x86_thread_state64_t.rip
+ break;
+ case CPU_TYPE_ARM:
+ startAddress = regs32[15]; // arm_thread_state_t.__pc
+ break;
+ case CPU_TYPE_ARM64:
+ startAddress = regs64[32]; // arm_thread_state64_t.__pc
+ break;
+ }
+ offset = (uint32_t)(startAddress - preferredLoadAddress());
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ // FIXME: validate offset is into executable segment
+ return (offset != 0);
+}
+
+bool MachOParser::canBePlacedInDyldCache(const std::string& path) const {
+ std::set<std::string> reasons;
+ return canBePlacedInDyldCache(path, reasons);
+}
+
+bool MachOParser::canBePlacedInDyldCache(const std::string& path, std::set<std::string>& reasons) const
+{
+ bool retval = true;
+ // only dylibs can go in cache
+ if ( fileType() != MH_DYLIB ) {
+ reasons.insert("Not MH_DYLIB");
+ return false; // cannot continue, installName() will assert() if not a dylib
+ }
+
+ // only dylibs built for /usr/lib or /System/Library can go in cache
+ const char* dylibName = installName();
+ if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
+ retval = false;
+ reasons.insert("Not in '/usr/lib/' or '/System/Library/'");
+ }
+
+ // flat namespace files cannot go in cache
+ if ( (header()->flags & MH_TWOLEVEL) == 0 ) {
+ retval = false;
+ reasons.insert("Not built with two level namespaces");
+ }
+
+ // don't put debug variants into dyld cache
+ if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
+ retval = false;
+ reasons.insert("Variant image");
+ }
+
+ // dylib must have extra info for moving DATA and TEXT segments apart
+ __block bool hasExtraInfo = false;
+ __block bool hasDyldInfo = false;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
+ hasExtraInfo = true;
+ if ( cmd->cmd == LC_DYLD_INFO_ONLY )
+ hasDyldInfo = true;
+ });
+ if ( !hasExtraInfo ) {
+ retval = false;
+ reasons.insert("Missing split seg info");
+ }
+ if ( !hasDyldInfo ) {
+ retval = false;
+ reasons.insert("Old binary, missing dyld info");
+ }
+
+ // dylib can only depend on other dylibs in the shared cache
+ __block bool allDepPathsAreGood = true;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( (strncmp(loadPath, "/usr/lib/", 9) != 0) && (strncmp(loadPath, "/System/Library/", 16) != 0) ) {
+ allDepPathsAreGood = false;
+ stop = true;
+ }
+ });
+ if ( !allDepPathsAreGood ) {
+ retval = false;
+ reasons.insert("Depends on cache inelegible dylibs");
+ }
+
+ // dylibs with interposing info cannot be in cache
+ __block bool hasInterposing = false;
+ forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop) {
+ hasInterposing = true;
+ });
+ if ( hasInterposing ) {
+ retval = false;
+ reasons.insert("Has interposing tuples");
+ }
+
+ return retval;
+}
+
+bool MachOParser::isDynamicExecutable() const
+{
+ if ( fileType() != MH_EXECUTE )
+ return false;
+
+ // static executables do not have dyld load command
+ __block bool hasDyldLoad = false;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_LOAD_DYLINKER ) {
+ hasDyldLoad = true;
+ stop = true;
+ }
+ });
+ return hasDyldLoad;
+}
+
+
+bool MachOParser::isSlideable() const
+{
+ if ( header()->filetype == MH_DYLIB )
+ return true;
+ if ( header()->filetype == MH_BUNDLE )
+ return true;
+ if ( (header()->filetype == MH_EXECUTE) && (header()->flags & MH_PIE) )
+ return true;
+
+ return false;
+}
+
+
+
+bool MachOParser::hasInitializer(Diagnostics& diag) const
+{
+ __block bool result = false;
+ forEachInitializer(diag, ^(uint32_t offset) {
+ result = true;
+ });
+ return result;
+}
+
+void MachOParser::forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+ __block uint64_t textSegAddrStart = 0;
+ __block uint64_t textSegAddrEnd = 0;
+
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__TEXT") == 0 ) {
+ textSegAddrStart = vmAddr;
+ textSegAddrEnd = vmAddr + vmSize;
+ stop = true;
+ }
+ });
+ if ( textSegAddrStart == textSegAddrEnd ) {
+ diag.error("no __TEXT segment");
+ return;
+ }
+
+ // if dylib linked with -init linker option, that initializer is first
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_ROUTINES ) {
+ const routines_command* routines = (routines_command*)cmd;
+ uint64_t dashInit = routines->init_address;
+ if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
+ callback((uint32_t)(dashInit - textSegAddrStart));
+ else
+ diag.error("-init does not point within __TEXT segment");
+ }
+ else if ( cmd->cmd == LC_ROUTINES_64 ) {
+ const routines_command_64* routines = (routines_command_64*)cmd;
+ uint64_t dashInit = routines->init_address;
+ if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
+ callback((uint32_t)(dashInit - textSegAddrStart));
+ else
+ diag.error("-init does not point within __TEXT segment");
+ }
+ });
+
+ // next any function pointers in mod-init section
+ bool p64 = is64();
+ unsigned pointerSize = p64 ? 8 : 4;
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+ if ( (flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+ if ( (size % pointerSize) != 0 ) {
+ diag.error("initializer section %s/%s has bad size", segmentName, sectionName);
+ stop = true;
+ return;
+ }
+ if ( illegalSectionSize ) {
+ diag.error("initializer section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+ stop = true;
+ return;
+ }
+ if ( ((long)content % pointerSize) != 0 ) {
+ diag.error("initializer section %s/%s is not pointer aligned", segmentName, sectionName);
+ stop = true;
+ return;
+ }
+ if ( p64 ) {
+ const uint64_t* initsStart = (uint64_t*)content;
+ const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + size);
+ for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
+ uint64_t anInit = *p;
+ if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
+ diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit);
+ stop = true;
+ break;
+ }
+ callback((uint32_t)(anInit - textSegAddrStart));
+ }
+ }
+ else {
+ const uint32_t* initsStart = (uint32_t*)content;
+ const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + size);
+ for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
+ uint32_t anInit = *p;
+ if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
+ diag.error("initializer 0x%0X does not point within __TEXT segment", anInit);
+ stop = true;
+ break;
+ }
+ callback(anInit - (uint32_t)textSegAddrStart);
+ }
+ }
+ }
+ });
+}
+
+void MachOParser::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+ if ( ( (flags & SECTION_TYPE) == S_DTRACE_DOF ) && !illegalSectionSize ) {
+ callback((uint32_t)((uintptr_t)content - (uintptr_t)header()));
+ }
+ });
+}
+
+
+uint32_t MachOParser::segmentCount() const
+{
+ __block uint32_t count = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ ++count;
+ });
+ return count;
+}
+
+void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const
+{
+ Diagnostics diag;
+ __block uint32_t segIndex = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
+ const section_64* const sectionsEnd = §ionsStart[segCmd->nsects];
+ for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+ if ( sect->align > p2align )
+ p2align = sect->align;
+ }
+ callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
+ const section* const sectionsEnd = §ionsStart[segCmd->nsects];
+ for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+ if ( sect->align > p2align )
+ p2align = sect->align;
+ }
+ callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachExportedSymbol(Diagnostics diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ if ( leInfo.dyldInfo != nullptr ) {
+ const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
+ const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size;
+ std::vector<ExportInfoTrie::Entry> exports;
+ if ( !ExportInfoTrie::parseTrie(trieStart, trieEnd, exports) ) {
+ diag.error("malformed exports trie");
+ return;
+ }
+ bool stop = false;
+ for (const ExportInfoTrie::Entry& exp : exports) {
+ bool isReExport = (exp.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
+ handler(exp.name.c_str(), exp.info.address, isReExport, stop);
+ if ( stop )
+ break;
+ }
+ }
+}
+
+bool MachOParser::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo,
+ bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const
+{
+ if ( !segIndexSet ) {
+ diag.error("%s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
+ return true;
+ }
+ if ( segmentIndex >= leInfo.layout.segmentCount ) {
+ diag.error("%s segment index %d too large", opcodeName, segmentIndex);
+ return true;
+ }
+ if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
+ diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
+ return true;
+ }
+ switch ( type ) {
+ case REBASE_TYPE_POINTER:
+ if ( !leInfo.layout.segments[segmentIndex].writable ) {
+ diag.error("%s pointer rebase is in non-writable segment", opcodeName);
+ return true;
+ }
+ if ( leInfo.layout.segments[segmentIndex].executable ) {
+ diag.error("%s pointer rebase is in executable segment", opcodeName);
+ return true;
+ }
+ break;
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ case REBASE_TYPE_TEXT_PCREL32:
+ if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
+ diag.error("%s text rebase is in segment that does not support text relocations", opcodeName);
+ return true;
+ }
+ if ( leInfo.layout.segments[segmentIndex].writable ) {
+ diag.error("%s text rebase is in writable segment", opcodeName);
+ return true;
+ }
+ if ( !leInfo.layout.segments[segmentIndex].executable ) {
+ diag.error("%s pointer rebase is in non-executable segment", opcodeName);
+ return true;
+ }
+ break;
+ default:
+ diag.error("%s unknown rebase type %d", opcodeName, type);
+ return true;
+ }
+ return false;
+}
+
+void MachOParser::forEachRebase(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ if ( leInfo.dyldInfo != nullptr ) {
+ // work around linker bug that laid down rebase opcodes for lazy pointer section when -bind_at_load used
+ __block int lpSegIndex = 0;
+ __block uint64_t lpSegOffsetStart = 0;
+ __block uint64_t lpSegOffsetEnd = 0;
+ bool hasWeakBinds = (leInfo.dyldInfo->weak_bind_size != 0);
+ if ( leInfo.dyldInfo->lazy_bind_size == 0 ) {
+ __block uint64_t lpAddr = 0;
+ __block uint64_t lpSize = 0;
+ forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
+ if ( (flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+ lpAddr = addr;
+ lpSize = size;
+ sectStop = true;
+ }
+ });
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& segStop) {
+ if ( (vmAddr <= lpAddr) && (vmAddr+vmSize >= lpAddr+lpSize) ) {
+ lpSegOffsetStart = lpAddr - vmAddr;
+ lpSegOffsetEnd = lpSegOffsetStart + lpSize;
+ segStop = true;
+ return;
+ }
+ ++lpSegIndex;
+ });
+ }
+ // don't remove rebase if there is a weak-bind at pointer location
+ bool (^weakBindAt)(uint64_t segOffset) = ^(uint64_t segOffset) {
+ if ( !hasWeakBinds )
+ return false;
+ __block bool result = false;
+ Diagnostics weakDiag;
+ forEachWeakDef(weakDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool& weakStop) {
+ if ( segOffset == dataSegOffset ) {
+ result = true;
+ weakStop = true;
+ }
+ });
+ return result;
+ };
+
+
+ const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+ const uint8_t* end = p + leInfo.dyldInfo->rebase_size;
+ const uint32_t pointerSize = (is64() ? 8 : 4);
+ uint8_t type = 0;
+ int segIndex = 0;
+ uint64_t segOffset = 0;
+ uint64_t count;
+ uint64_t skip;
+ bool segIndexSet = false;
+ bool stop = false;
+ while ( !stop && diag.noError() && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ stop = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segOffset = read_uleb128(diag, p, end);
+ segIndexSet = true;
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(diag, p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*pointerSize;
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+ return;
+ if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
+ handler(segIndex, segOffset, type, stop);
+ segOffset += pointerSize;
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(diag, p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+ return;
+ if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
+ handler(segIndex, segOffset, type, stop);
+ segOffset += pointerSize;
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+ return;
+ handler(segIndex, segOffset, type, stop);
+ segOffset += read_uleb128(diag, p, end) + pointerSize;
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(diag, p, end);
+ if ( diag.hasError() )
+ break;
+ skip = read_uleb128(diag, p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+ return;
+ handler(segIndex, segOffset, type, stop);
+ segOffset += skip + pointerSize;
+ }
+ break;
+ default:
+ diag.error("unknown rebase opcode 0x%02X", opcode);
+ }
+ }
+ }
+ else {
+ // old binary
+ const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->locreloff);
+ const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nlocrel];
+ bool stop = false;
+ const uint8_t relocSize = (is64() ? 3 : 2);
+ for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
+ if ( reloc->r_length != relocSize ) {
+ diag.error("local relocation has wrong r_length");
+ break;
+ }
+ if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
+ diag.error("local relocation has wrong r_type");
+ break;
+ }
+ doLocalReloc(diag, reloc->r_address, stop, handler);
+ }
+ // then process indirect symbols
+ forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+ if ( !bind && !bindLazy )
+ handler(segIndex, segOffset, REBASE_TYPE_POINTER, indStop);
+ });
+ }
+}
+
+bool MachOParser::doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
+{
+ bool firstWritable = (header()->cputype == CPU_TYPE_X86_64);
+ __block uint64_t relocBaseAddress = 0;
+ __block bool baseFound = false;
+ __block uint32_t segIndex = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
+ if ( !baseFound ) {
+ if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
+ baseFound = true;
+ relocBaseAddress = vmAddr;
+ }
+ }
+ if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
+ uint8_t type = REBASE_TYPE_POINTER;
+ uint64_t segOffset = relocBaseAddress + r_address - vmAddr;
+ handler(segIndex, segOffset, type, stop);
+ stopSeg = true;
+ }
+ ++segIndex;
+ });
+
+ return false;
+}
+
+int MachOParser::libOrdinalFromDesc(uint16_t n_desc) const
+{
+ // -flat_namespace is always flat lookup
+ if ( (header()->flags & MH_TWOLEVEL) == 0 )
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+ // extract byte from undefined symbol entry
+ int libIndex = GET_LIBRARY_ORDINAL(n_desc);
+ switch ( libIndex ) {
+ case SELF_LIBRARY_ORDINAL:
+ return BIND_SPECIAL_DYLIB_SELF;
+
+ case DYNAMIC_LOOKUP_ORDINAL:
+ return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+ case EXECUTABLE_ORDINAL:
+ return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
+ }
+
+ return libIndex;
+}
+
+bool MachOParser::doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop,
+ void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+ uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
+{
+ const bool firstWritable = (header()->cputype == CPU_TYPE_X86_64);
+ const bool is64Bit = is64();
+ __block uint64_t relocBaseAddress = 0;
+ __block bool baseFound = false;
+ __block uint32_t segIndex = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
+ if ( !baseFound ) {
+ if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
+ baseFound = true;
+ relocBaseAddress = vmAddr;
+ }
+ }
+ if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
+ uint8_t type = BIND_TYPE_POINTER;
+ uint64_t segOffset = relocBaseAddress + r_address - vmAddr;
+ const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
+ const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
+ const struct nlist* symbols32 = (struct nlist*)symbolTable;
+ const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+ uint32_t symCount = leInfo.symTab->nsyms;
+ uint32_t poolSize = leInfo.symTab->strsize;
+ if ( r_symbolnum < symCount ) {
+ uint16_t n_desc = is64Bit ? symbols64[r_symbolnum].n_desc : symbols32[r_symbolnum].n_desc;
+ uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+ uint32_t strOffset = is64Bit ? symbols64[r_symbolnum].n_un.n_strx : symbols32[r_symbolnum].n_un.n_strx;
+ if ( strOffset < poolSize ) {
+ const char* symbolName = stringPool + strOffset;
+ bool weakImport = (n_desc & N_WEAK_REF);
+ bool lazy = false;
+ uint64_t addend = is64Bit ? (*((uint64_t*)((char*)header()+fileOffset+segOffset))) : (*((uint32_t*)((char*)header()+fileOffset+segOffset)));
+ handler(segIndex, segOffset, type, libOrdinal, addend, symbolName, weakImport, lazy, stop);
+ stopSeg = true;
+ }
+ }
+ }
+ ++segIndex;
+ });
+
+ return false;
+}
+
+bool MachOParser::invalidBindState(Diagnostics& diag, const char* opcodeName, const LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet,
+ uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
+{
+ if ( !segIndexSet ) {
+ diag.error("%s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
+ return true;
+ }
+ if ( segmentIndex >= leInfo.layout.segmentCount ) {
+ diag.error("%s segment index %d too large", opcodeName, segmentIndex);
+ return true;
+ }
+ if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
+ diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
+ return true;
+ }
+ if ( symbolName == NULL ) {
+ diag.error("%s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", opcodeName);
+ return true;
+ }
+ if ( !libraryOrdinalSet ) {
+ diag.error("%s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", opcodeName);
+ return true;
+ }
+ if ( libOrdinal > (int)dylibCount ) {
+ diag.error("%s has library ordinal too large (%d) max (%d)", opcodeName, libOrdinal, dylibCount);
+ return true;
+ }
+ if ( libOrdinal < -2 ) {
+ diag.error("%s has unknown library special ordinal (%d)", opcodeName, libOrdinal);
+ return true;
+ }
+ switch ( type ) {
+ case BIND_TYPE_POINTER:
+ if ( !leInfo.layout.segments[segmentIndex].writable ) {
+ diag.error("%s pointer bind is in non-writable segment", opcodeName);
+ return true;
+ }
+ if ( leInfo.layout.segments[segmentIndex].executable ) {
+ diag.error("%s pointer bind is in executable segment", opcodeName);
+ return true;
+ }
+ break;
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ case BIND_TYPE_TEXT_PCREL32:
+ if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
+ diag.error("%s text bind is in segment that does not support text relocations", opcodeName);
+ return true;
+ }
+ if ( leInfo.layout.segments[segmentIndex].writable ) {
+ diag.error("%s text bind is in writable segment", opcodeName);
+ return true;
+ }
+ if ( !leInfo.layout.segments[segmentIndex].executable ) {
+ diag.error("%s pointer bind is in non-executable segment", opcodeName);
+ return true;
+ }
+ break;
+ default:
+ diag.error("%s unknown bind type %d", opcodeName, type);
+ return true;
+ }
+ return false;
+}
+
+void MachOParser::forEachBind(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type,
+ int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+ const uint32_t dylibCount = dependentDylibCount();
+
+ if ( leInfo.dyldInfo != nullptr ) {
+ // process bind opcodes
+ const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
+ const uint8_t* end = p + leInfo.dyldInfo->bind_size;
+ const uint32_t pointerSize = (is64() ? 8 : 4);
+ uint8_t type = 0;
+ uint64_t segmentOffset = 0;
+ uint8_t segmentIndex = 0;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ bool segIndexSet = false;
+ bool libraryOrdinalSet = false;
+
+ int64_t addend = 0;
+ uint64_t count;
+ uint64_t skip;
+ bool weakImport = false;
+ bool done = false;
+ bool stop = false;
+ while ( !done && !stop && diag.noError() && (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;
+ libraryOrdinalSet = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = (int)read_uleb128(diag, p, end);
+ libraryOrdinalSet = true;
+ 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;
+ }
+ libraryOrdinalSet = true;
+ 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(diag, p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(diag, p, end);
+ segIndexSet = true;
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segmentOffset += read_uleb128(diag, p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+ segmentOffset += pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+ segmentOffset += read_uleb128(diag, p, end) + pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+ segmentOffset += immediate*pointerSize + pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(diag, p, end);
+ skip = read_uleb128(diag, p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+ segmentOffset += skip + pointerSize;
+ }
+ break;
+ default:
+ diag.error("bad bind opcode 0x%02X", *p);
+ }
+ }
+ if ( diag.hasError() || stop )
+ return;
+ // process lazy bind opcodes
+ if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
+ p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
+ end = p + leInfo.dyldInfo->lazy_bind_size;
+ type = BIND_TYPE_POINTER;
+ segmentOffset = 0;
+ segmentIndex = 0;
+ symbolName = NULL;
+ libraryOrdinal = 0;
+ segIndexSet = false;
+ libraryOrdinalSet= false;
+ addend = 0;
+ weakImport = false;
+ stop = false;
+ while ( !stop && diag.noError() && (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;
+ libraryOrdinalSet = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = (int)read_uleb128(diag, p, end);
+ libraryOrdinalSet = true;
+ 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;
+ }
+ libraryOrdinalSet = true;
+ 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(diag, p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(diag, p, end);
+ segIndexSet = true;
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, true, stop);
+ segmentOffset += pointerSize;
+ 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:
+ diag.error("bad lazy bind opcode 0x%02X", opcode);
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // old binary, first process relocation
+ const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
+ const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nextrel];
+ bool stop = false;
+ const uint8_t relocSize = (is64() ? 3 : 2);
+ for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
+ if ( reloc->r_length != relocSize ) {
+ diag.error("external relocation has wrong r_length");
+ break;
+ }
+ if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
+ diag.error("external relocation has wrong r_type");
+ break;
+ }
+ doExternalReloc(diag, reloc->r_address, reloc->r_symbolnum, leInfo, stop, handler);
+ }
+ // then process indirect symbols
+ forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+ if ( bind )
+ handler(segIndex, segOffset, (selfModifyingStub ? BIND_TYPE_IMPORT_JMP_REL32 : BIND_TYPE_POINTER), bindLibOrdinal, 0, bindSymbolName, bindWeakImport, bindLazy, indStop);
+ });
+ }
+}
+
+
+void MachOParser::forEachWeakDef(Diagnostics& diag, void (^handler)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset,
+ uint64_t addend, const char* symbolName, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ const uint32_t dylibCount = dependentDylibCount();
+ if ( leInfo.dyldInfo != nullptr ) {
+ // process weak bind opcodes
+ const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
+ const uint8_t* end = p + leInfo.dyldInfo->weak_bind_size;
+ const uint32_t pointerSize = (is64() ? 8 : 4);
+ uint8_t type = 0;
+ uint64_t segmentOffset = 0;
+ uint8_t segmentIndex = 0;
+ const char* symbolName = NULL;
+ int64_t addend = 0;
+ uint64_t count;
+ uint64_t skip;
+ bool segIndexSet = false;
+ bool done = false;
+ bool stop = false;
+ while ( !done && !stop && diag.noError() && (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:
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ diag.error("unexpected dylib ordinal in weak binding info");
+ return;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 )
+ handler(true, 0, 0, 0, symbolName, stop);
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(diag, p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(diag, p, end);
+ segIndexSet = true;
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segmentOffset += read_uleb128(diag, p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+ segmentOffset += pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+ segmentOffset += read_uleb128(diag, p, end) + pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+ segmentOffset += immediate*pointerSize + pointerSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(diag, p, end);
+ skip = read_uleb128(diag, p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+ return;
+ handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+ segmentOffset += skip + pointerSize;
+ }
+ break;
+ default:
+ diag.error("bad weak bind opcode 0x%02X", *p);
+ }
+ }
+ if ( diag.hasError() || stop )
+ return;
+ }
+ else {
+ // old binary
+ //assert(0 && "weak defs not supported for old binaries yet");
+ }
+}
+
+
+
+void MachOParser::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ // find lazy and non-lazy pointer sections
+ const bool is64Bit = is64();
+ const uint32_t* const indirectSymbolTable = (uint32_t*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->indirectsymoff);
+ const uint32_t indirectSymbolTableCount = leInfo.dynSymTab->nindirectsyms;
+ const uint32_t pointerSize = is64Bit ? 8 : 4;
+ const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
+ const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
+ const struct nlist* symbols32 = (struct nlist*)symbolTable;
+ const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+ uint32_t symCount = leInfo.symTab->nsyms;
+ uint32_t poolSize = leInfo.symTab->strsize;
+ __block bool stop = false;
+ forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content,
+ uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectionStop) {
+ uint8_t sectionType = (flags & SECTION_TYPE);
+ if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && (sectionType != S_SYMBOL_STUBS) )
+ return;
+ bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (flags & S_ATTR_SELF_MODIFYING_CODE) && (reserved2 == 5) && (header()->cputype == CPU_TYPE_I386);
+ if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
+ diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
+ sectionStop = true;
+ return;
+ }
+ uint32_t elementSize = selfModifyingStub ? reserved2 : pointerSize;
+ uint32_t elementCount = (uint32_t)(size/elementSize);
+ if (greaterThanAddOrOverflow(reserved1, elementCount, indirectSymbolTableCount)) {
+ diag.error("section %s overflows indirect symbol table", sectionName);
+ sectionStop = true;
+ return;
+ }
+ __block uint32_t index = 0;
+ __block uint32_t segIndex = 0;
+ __block uint64_t sectionSegOffset;
+ forEachSegment(^(const char* segmentName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &segStop) {
+ if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
+ sectionSegOffset = addr - vmAddr;
+ segIndex = index;
+ segStop = true;
+ }
+ ++index;
+ });
+
+ for (int i=0; (i < elementCount) && !stop; ++i) {
+ uint32_t symNum = indirectSymbolTable[reserved1 + i];
+ if ( symNum == INDIRECT_SYMBOL_ABS )
+ continue;
+ uint64_t segOffset = sectionSegOffset+i*elementSize;
+ if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
+ handler(segIndex, segOffset, false, 0, "", false, false, false, stop);
+ continue;
+ }
+ if ( symNum > symCount ) {
+ diag.error("indirect symbol[%d] = %d which is invalid symbol index", reserved1 + i, symNum);
+ sectionStop = true;
+ return;
+ }
+ uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
+ uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+ uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
+ if ( strOffset > poolSize ) {
+ diag.error("symbol[%d] string offset out of range", reserved1 + i);
+ sectionStop = true;
+ return;
+ }
+ const char* symbolName = stringPool + strOffset;
+ bool weakImport = (n_desc & N_WEAK_REF);
+ bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS);
+ handler(segIndex, segOffset, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
+ }
+ sectionStop = stop;
+ });
+}
+
+void MachOParser::forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const
+{
+ const bool is64Bit = is64();
+ const unsigned entrySize = is64Bit ? 16 : 8;
+ const unsigned pointerSize = is64Bit ? 8 : 4;
+ forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& secStop) {
+ if ( ((flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sectionName, "__interpose") == 0) && (strcmp(segmentName, "__DATA") == 0)) ) {
+ if ( (size % entrySize) != 0 ) {
+ diag.error("interposing section %s/%s has bad size", segmentName, sectionName);
+ secStop = true;
+ return;
+ }
+ if ( illegalSectionSize ) {
+ diag.error("interposing section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+ secStop = true;
+ return;
+ }
+ if ( ((long)content % pointerSize) != 0 ) {
+ diag.error("interposing section %s/%s is not pointer aligned", segmentName, sectionName);
+ secStop = true;
+ return;
+ }
+ __block uint32_t sectionSegIndex = 0;
+ __block uint64_t sectionSegOffset = 0;
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& segStop) {
+ if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
+ sectionSegIndex = segIndex;
+ sectionSegOffset = addr - vmAddr;
+ segStop = true;
+ }
+ });
+ if ( sectionSegIndex == 0 ) {
+ diag.error("interposing section %s/%s is not in a segment", segmentName, sectionName);
+ secStop = true;
+ return;
+ }
+ uint32_t offset = 0;
+ bool tupleStop = false;
+ for (int i=0; i < (size/entrySize); ++i) {
+ uint64_t replacementContent = is64Bit ? (*(uint64_t*)((char*)content + offset)) : (*(uint32_t*)((char*)content + offset));
+ handler(sectionSegIndex, sectionSegOffset+offset, sectionSegOffset+offset+pointerSize, replacementContent, tupleStop);
+ offset += entrySize;
+ if ( tupleStop )
+ break;
+ }
+ }
+ });
+}
+
+
+const void* MachOParser::content(uint64_t vmOffset)
+{
+ __block const void* result = nullptr;
+ __block uint32_t firstSegFileOffset = 0;
+ __block uint64_t firstSegVmAddr = 0;
+ if ( isRaw() ) {
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
+ if ( firstSegFileOffset == 0) {
+ if ( fileSize == 0 )
+ return; // skip __PAGEZERO
+ firstSegFileOffset = fileOffset;
+ firstSegVmAddr = vmAddr;
+ }
+ uint64_t segVmOffset = vmAddr - firstSegVmAddr;
+ if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
+ result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
+ stop = true;
+ }
+ });
+ }
+ else if ( inRawCache() ) {
+ forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
+ if ( firstSegFileOffset == 0 ) {
+ firstSegFileOffset = fileOffset;
+ firstSegVmAddr = vmAddr;
+ }
+ uint64_t segVmOffset = vmAddr - firstSegVmAddr;
+ if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
+ result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
+ stop = true;
+ }
+ });
+ }
+ else {
+ // non-raw cache is easy
+ result = (char*)(header()) + vmOffset;
+ }
+ return result;
+}
+
+#endif // !DYLD_IN_PROCESS
+
+bool MachOParser::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size)
+{
+ textOffset = 0;
+ size = 0;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+ const encryption_info_command* encCmd = (encryption_info_command*)cmd;
+ if ( encCmd->cryptid == 1 ) {
+ // Note: cryptid is 0 in just-built apps. The iTunes App Store sets cryptid to 1
+ textOffset = encCmd->cryptoff;
+ size = encCmd->cryptsize;
+ }
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return (textOffset != 0);
+}
+
+bool MachOParser::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20])
+{
+ const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(codeSigStart, codeSignLen);
+ if ( cd == nullptr )
+ return false;
+
+ uint32_t cdLength = htonl(cd->length);
+ if ( cd->hashType == CS_HASHTYPE_SHA256 ) {
+ uint8_t digest[CC_SHA256_DIGEST_LENGTH];
+ CC_SHA256(cd, cdLength, digest);
+ // cd-hash of sigs that use SHA256 is the first 20 bytes of the SHA256 of the code digest
+ memcpy(cdHash, digest, 20);
+ return true;
+ }
+ else if ( cd->hashType == CS_HASHTYPE_SHA1 ) {
+ // compute hash directly into return buffer
+ CC_SHA1(cd, cdLength, cdHash);
+ return true;
+ }
+
+ return false;
+}
+
+const void* MachOParser::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen)
+{
+ // verify min length of overall code signature
+ if ( codeSignLen < sizeof(CS_SuperBlob) )
+ return nullptr;
+
+ // verify magic at start
+ const CS_SuperBlob* codeSuperBlob = (CS_SuperBlob*)codeSigStart;
+ if ( codeSuperBlob->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE) )
+ return nullptr;
+
+ // verify count of sub-blobs not too large
+ uint32_t subBlobCount = htonl(codeSuperBlob->count);
+ if ( (codeSignLen-sizeof(CS_SuperBlob))/sizeof(CS_BlobIndex) < subBlobCount )
+ return nullptr;
+
+ // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
+ for (uint32_t i=0; i < subBlobCount; ++i) {
+ if ( codeSuperBlob->index[i].type != htonl(CSSLOT_CODEDIRECTORY) )
+ continue;
+ uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset);
+ // verify offset is not out of range
+ if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) )
+ return nullptr;
+ const CS_CodeDirectory* cd = (CS_CodeDirectory*)((uint8_t*)codeSuperBlob + cdOffset);
+ uint32_t cdLength = htonl(cd->length);
+ // verify code directory length not out of range
+ if ( cdLength > (codeSignLen - cdOffset) )
+ return nullptr;
+ if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) )
+ return cd;
+ }
+ return nullptr;
+}
+
+
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 MachOParser_h
+#define MachOParser_h
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "Diagnostics.h"
+
+
+#define BIND_TYPE_IMPORT_JMP_REL32 4
+
+namespace dyld3 {
+
+// Note, this should make PLATFORM_* values in <mach-o/loader.h>
+enum class Platform {
+ unknown = 0,
+ macOS = 1,
+ iOS = 2,
+ tvOS = 3,
+ watchOS = 4,
+ bridgeOS = 5
+};
+
+struct VIS_HIDDEN UUID {
+ UUID() {}
+ UUID(const UUID& other) { uuid_copy(&_bytes[0], &other._bytes[0]); }
+ UUID(const uuid_t other_uuid) { uuid_copy(&_bytes[0], other_uuid); }
+ bool operator<(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) < 0; }
+ bool operator==(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) == 0; }
+ bool operator!=(const UUID& other) const { return !(*this == other); }
+
+ size_t hash() const
+ {
+ size_t retval = 0;
+ for (auto i = 0; i < 16 / sizeof(size_t); ++i) {
+ retval ^= ((size_t*)(&_bytes[0]))[i];
+ }
+ return retval;
+ }
+ const unsigned char* get() const { return &_bytes[0]; };
+private:
+ std::array<unsigned char, 16> _bytes;
+};
+
+class VIS_HIDDEN MachOParser
+{
+public:
+#if !DYLD_IN_PROCESS
+ static bool isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables);
+ static bool isArch(const mach_header* mh, const std::string& archName);
+ static std::string archName(uint32_t cputype, uint32_t cpusubtype);
+ static std::string platformName(Platform platform);
+ static std::string versionString(uint32_t packedVersion);
+ static uint32_t cpuTypeFromArchName(const std::string& archName);
+ static uint32_t cpuSubtypeFromArchName(const std::string& archName);
+#else
+ static bool isMachO(Diagnostics& diag, const void* fileContent, size_t fileLength);
+ static bool wellFormedMachHeaderAndLoadCommands(const mach_header* mh);
+#endif
+ MachOParser(const mach_header* mh, bool dyldCacheIsRaw=false);
+ bool valid(Diagnostics& diag);
+
+ const mach_header* header() const;
+ uint32_t fileType() const;
+ std::string archName() const;
+ bool is64() const;
+ bool inDyldCache() const;
+ bool hasThreadLocalVariables() const;
+ Platform platform() const;
+ uint64_t preferredLoadAddress() const;
+ UUID uuid() const;
+ bool getUuid(uuid_t uuid) const;
+ bool getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const;
+ bool isSimulatorBinary() const;
+ bool getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
+ const char* installName() const;
+ uint32_t dependentDylibCount() const;
+ const char* dependentDylibLoadPath(uint32_t depIndex) const;
+ void forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
+ void forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop)) const;
+ void forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const;
+ void forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const;
+ void forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const;
+ void forEachRPath(void (^callback)(const char* rPath, bool& stop)) const;
+ void forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content,
+ uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const;
+
+ struct FoundSymbol {
+ enum class Kind { headerOffset, absolute, resolverOffset };
+ Kind kind;
+ bool isThreadLocal;
+ const mach_header* foundInDylib;
+ void* foundExtra;
+ uint64_t value;
+ uint32_t resolverFuncOffset;
+ const char* foundSymbolName;
+ };
+
+ typedef bool (^DependentFinder)(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra);
+
+ bool findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder finder) const;
+ bool findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const;
+ bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size);
+
+#if DYLD_IN_PROCESS
+ intptr_t getSlide() const;
+ bool hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const;
+ bool findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const;
+ const char* segmentName(uint32_t segIndex) const;
+#else
+
+ bool uses16KPages() const;
+ bool hasObjC() const;
+ bool hasWeakDefs() const;
+ bool isEncrypted() const;
+ bool hasPlusLoadMethod(Diagnostics& diag) const;
+ bool hasInitializer(Diagnostics& diag) const;
+ bool getCDHash(uint8_t cdHash[20]);
+ bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size);
+ bool usesLibraryValidation() const;
+ bool isRestricted() const;
+ bool getEntry(uint32_t& offset, bool& usesCRT);
+ bool canBePlacedInDyldCache(const std::string& path) const;
+ bool canBePlacedInDyldCache(const std::string& path, std::set<std::string>& reasons) const;
+ bool isDynamicExecutable() const;
+ bool isSlideable() const;
+ void forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
+ void forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
+ uint32_t segmentCount() const;
+ void forEachExportedSymbol(Diagnostics diag, void (^callback)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const;
+ void forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const;
+ void forEachRebase(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const;
+ void forEachBind(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+ uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const;
+ void forEachWeakDef(Diagnostics& diag, void (^callback)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset,
+ uint64_t addend, const char* symbolName, bool& stop)) const;
+ void forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const;
+ void forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const;
+ const void* content(uint64_t vmOffset);
+#endif
+
+ static const uint8_t* trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);
+ static uint64_t read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
+ static int64_t read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
+ static bool cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]);
+ static Platform currentPlatform();
+
+private:
+ struct LayoutInfo {
+#if DYLD_IN_PROCESS
+ uintptr_t slide;
+ uintptr_t textUnslidVMAddr;
+ uintptr_t linkeditUnslidVMAddr;
+ uint32_t linkeditFileOffset;
+#else
+ uint32_t segmentCount;
+ uint32_t linkeditSegIndex;
+ struct {
+ uint64_t mappingOffset;
+ uint64_t fileOffset;
+ uint64_t fileSize;
+ uint64_t segUnslidAddress;
+ uint64_t writable : 1,
+ executable : 1,
+ textRelocsAllowed : 1, // segment supports text relocs (i386 only)
+ segSize : 61;
+ } segments[128];
+#endif
+ };
+
+ struct LinkEditInfo
+ {
+ const dyld_info_command* dyldInfo;
+ const symtab_command* symTab;
+ const dysymtab_command* dynSymTab;
+ const linkedit_data_command* splitSegInfo;
+ const linkedit_data_command* functionStarts;
+ const linkedit_data_command* dataInCode;
+ const linkedit_data_command* codeSig;
+ LayoutInfo layout;
+ };
+
+ void getLinkEditPointers(Diagnostics& diag, LinkEditInfo&) const;
+ void getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const;
+ void getLayoutInfo(LayoutInfo&) const;
+ const uint8_t* getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const;
+
+#if !DYLD_IN_PROCESS
+ struct ArchInfo
+ {
+ const char* name;
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ };
+ static const ArchInfo _s_archInfos[];
+
+ const uint8_t* getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
+ bool doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const;
+ uint8_t relocPointerType() const;
+ int libOrdinalFromDesc(uint16_t n_desc) const;
+ bool doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop,
+ void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+ uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const;
+ bool validLoadCommands(Diagnostics& diag, size_t fileLen);
+ bool validEmbeddedPaths(Diagnostics& diag);
+ bool validSegments(Diagnostics& diag, size_t fileLen);
+ bool validLinkeditLayout(Diagnostics& diag);
+ bool invalidBindState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet,
+ uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const;
+ bool invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet,
+ uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const;
+#endif
+ static const void* findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen);
+ void forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+ uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const;
+
+ void forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
+ bool isRaw() const;
+ bool inRawCache() const;
+
+ long _data; // if low bit true, then this is raw file (not loaded image)
+};
+
+
+
+class VIS_HIDDEN FatUtil
+{
+public:
+ static bool isFatFile(const void* fileStart);
+ static void forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop));
+#if !DYLD_IN_PROCESS
+ static bool isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice);
+#endif
+};
+
+
+} // namespace dyld3
+
+namespace std {
+template <>
+struct hash<dyld3::UUID> {
+ size_t operator()(const dyld3::UUID& x) const
+ {
+ return x.hash();
+ }
+};
+}
+
+#endif // MachOParser_h
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <unistd.h>
+
+#include "PathOverrides.h"
+
+
+
+namespace dyld3 {
+
+#if BUILDING_LIBDYLD
+PathOverrides gPathOverrides;
+#endif
+
+
+// based on ANSI-C strstr()
+static const char* strrstr(const char* str, const char* sub)
+{
+ const size_t sublen = strlen(sub);
+ for(const char* p = &str[strlen(str)]; p != str; --p) {
+ if ( strncmp(p, sub, sublen) == 0 )
+ return p;
+ }
+ return NULL;
+}
+
+
+#if DYLD_IN_PROCESS
+void PathOverrides::setEnvVars(const char* envp[])
+{
+ for (const char** p = envp; *p != NULL; p++) {
+ addEnvVar(*p);
+ }
+}
+
+#else
+PathOverrides::PathOverrides(const std::vector<std::string>& env)
+{
+ for (const std::string& envVar : env) {
+ addEnvVar(envVar.c_str());
+ }
+}
+#endif
+
+#if !BUILDING_LIBDYLD
+// libdyld is never unloaded
+PathOverrides::~PathOverrides()
+{
+ freeArray(_dylibPathOverrides);
+ freeArray(_frameworkPathOverrides);
+ freeArray(_frameworkPathFallbacks);
+ freeArray(_dylibPathFallbacks);
+}
+#endif
+
+
+void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const
+{
+ if ( value == nullptr )
+ return;
+ size_t allocSize = strlen(key) + strlen(value) + 2;
+ char buffer[allocSize];
+ strlcpy(buffer, key, allocSize);
+ strlcat(buffer, "=", allocSize);
+ strlcat(buffer, value, allocSize);
+ handler(buffer);
+}
+
+void PathOverrides::handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const
+{
+ if ( list == nullptr )
+ return;
+ size_t allocSize = strlen(key) + 2;
+ for (const char** lp=list; *lp != nullptr; ++lp)
+ allocSize += strlen(*lp)+1;
+ char buffer[allocSize];
+ strlcpy(buffer, key, allocSize);
+ strlcat(buffer, "=", allocSize);
+ bool needColon = false;
+ for (const char** lp=list; *lp != nullptr; ++lp) {
+ if ( needColon )
+ strlcat(buffer, ":", allocSize);
+ strlcat(buffer, *lp, allocSize);
+ needColon = true;
+ }
+ handler(buffer);
+}
+
+void PathOverrides::forEachEnvVar(void (^handler)(const char* envVar)) const
+{
+ handleListEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverrides, handler);
+ handleListEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverrides, handler);
+ handleListEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks, handler);
+ handleListEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacks, handler);
+ handleListEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs, handler);
+ handleEnvVar( "DYLD_IMAGE_SUFFIX", _imageSuffix, handler);
+ handleEnvVar( "DYLD_ROOT_PATH", _rootPath, handler);
+}
+
+uint32_t PathOverrides::envVarCount() const
+{
+ uint32_t count = 0;
+ if ( _dylibPathOverrides != nullptr )
+ ++count;
+ if ( _frameworkPathOverrides != nullptr )
+ ++count;
+ if ( _frameworkPathFallbacks != nullptr )
+ ++count;
+ if ( _dylibPathFallbacks != nullptr )
+ ++count;
+ if ( _insertedDylibs != nullptr )
+ ++count;
+ if ( _imageSuffix != nullptr )
+ ++count;
+ if ( _rootPath != nullptr )
+ ++count;
+ return count;
+}
+
+void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath)) const
+{
+ if ( _insertedDylibs == nullptr )
+ return;
+ for (const char** lp=_insertedDylibs; *lp != nullptr; ++lp)
+ handler(*lp);
+}
+
+void PathOverrides::addEnvVar(const char* keyEqualsValue)
+{
+ const char* equals = strchr(keyEqualsValue, '=');
+ if ( equals != NULL ) {
+ const char* value = &equals[1];
+ const size_t keyLen = equals-keyEqualsValue;
+ char key[keyLen+1];
+ strncpy(key, keyEqualsValue, keyLen);
+ key[keyLen] = '\0';
+ if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) {
+ _dylibPathOverrides = parseColonListIntoArray(value);
+ }
+ else if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
+ _frameworkPathOverrides = parseColonListIntoArray(value);
+ }
+ else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
+ _frameworkPathFallbacks = parseColonListIntoArray(value);
+ }
+ else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
+ _dylibPathFallbacks = parseColonListIntoArray(value);
+ }
+ else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
+ _insertedDylibs = parseColonListIntoArray(value);
+ }
+ else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
+ _imageSuffix = value;
+ }
+ else if ( strcmp(key, "DYLD_ROOT_PATH") == 0 ) {
+ _rootPath = value;
+ }
+ }
+}
+
+void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path))
+{
+ char buffer[strlen(list)+1];
+ const char* t = list;
+ for (const char* s=list; *s != '\0'; ++s) {
+ if (*s != ':')
+ continue;
+ size_t len = s - t;
+ memcpy(buffer, t, len);
+ buffer[len] = '\0';
+ handler(buffer);
+ t = s+1;
+ }
+ handler(t);
+}
+
+const char** PathOverrides::parseColonListIntoArray(const char* list)
+{
+ __block int count = 1;
+ forEachInColonList(list, ^(const char* path) {
+ ++count;
+ });
+ const char** array = (const char**)malloc(count*sizeof(char*));
+ __block const char** p = array;
+ forEachInColonList(list, ^(const char* path) {
+ *p++ = strdup(path);
+ });
+ *p = nullptr;
+ return array;
+}
+
+void PathOverrides::freeArray(const char** array)
+{
+ if ( array == nullptr )
+ return;
+
+ for (const char** p=array; *p != nullptr; ++p) {
+ free((void*)*p);
+ }
+ free(array);
+}
+
+void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
+{
+ bool stop = false;
+ if ( _dylibPathFallbacks != nullptr ) {
+ for (const char** fp=_dylibPathFallbacks; *fp != nullptr; ++fp) {
+ handler(*fp, stop);
+ if ( stop )
+ return;
+ }
+ }
+ else {
+ switch ( platform ) {
+ case Platform::macOS:
+ // "$HOME/lib"
+ handler("/usr/local/lib", stop); // FIXME: not for restricted processes
+ if ( !stop )
+ handler("/usr/lib", stop);
+ break;
+ case Platform::iOS:
+ case Platform::watchOS:
+ case Platform::tvOS:
+ case Platform::bridgeOS:
+ case Platform::unknown:
+ handler("/usr/local/lib", stop);
+ if ( !stop )
+ handler("/usr/lib", stop);
+ break;
+ }
+ }
+}
+
+void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
+{
+ bool stop = false;
+ if ( _frameworkPathFallbacks != nullptr ) {
+ for (const char** fp=_frameworkPathFallbacks; *fp != nullptr; ++fp) {
+ handler(*fp, stop);
+ if ( stop )
+ return;
+ }
+ }
+ else {
+ switch ( platform ) {
+ case Platform::macOS:
+ // "$HOME/Library/Frameworks"
+ handler("/Library/Frameworks", stop); // FIXME: not for restricted processes
+ // "/Network/Library/Frameworks"
+ if ( !stop )
+ handler("/System/Library/Frameworks", stop);
+ break;
+ case Platform::iOS:
+ case Platform::watchOS:
+ case Platform::tvOS:
+ case Platform::bridgeOS:
+ case Platform::unknown:
+ handler("/System/Library/Frameworks", stop);
+ break;
+ }
+ }
+}
+
+void PathOverrides::forEachPathVariant(const char* initialPath,
+#if !DYLD_IN_PROCESS
+ Platform platform,
+#endif
+ void (^handler)(const char* possiblePath, bool& stop)) const
+{
+#if DYLD_IN_PROCESS
+ Platform platform = MachOParser::currentPlatform();
+#endif
+ __block bool stop = false;
+
+ // check for overrides
+ const char* frameworkPartialPath = getFrameworkPartialPath(initialPath);
+ if ( frameworkPartialPath != nullptr ) {
+ const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
+ // look at each DYLD_FRAMEWORK_PATH directory
+ if ( _frameworkPathOverrides != nullptr ) {
+ for (const char** fp=_frameworkPathOverrides; *fp != nullptr; ++fp) {
+ char npath[strlen(*fp)+frameworkPartialPathLen+8];
+ strcpy(npath, *fp);
+ strcat(npath, "/");
+ strcat(npath, frameworkPartialPath);
+ handler(npath, stop);
+ if ( stop )
+ return;
+ }
+ }
+ }
+ else {
+ const char* libraryLeafName = getLibraryLeafName(initialPath);
+ const size_t libraryLeafNameLen = strlen(libraryLeafName);
+ // look at each DYLD_LIBRARY_PATH directory
+ if ( _dylibPathOverrides != nullptr ) {
+ for (const char** lp=_dylibPathOverrides; *lp != nullptr; ++lp) {
+ char libpath[strlen(*lp)+libraryLeafNameLen+8];
+ strcpy(libpath, *lp);
+ strcat(libpath, "/");
+ strcat(libpath, libraryLeafName);
+ handler(libpath, stop);
+ if ( stop )
+ return;
+ }
+ }
+ }
+
+ // try original path
+ handler(initialPath, stop);
+ if ( stop )
+ return;
+
+ // check fallback paths
+ if ( frameworkPartialPath != nullptr ) {
+ const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
+ // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
+ forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
+ char npath[strlen(dir)+frameworkPartialPathLen+8];
+ strcpy(npath, dir);
+ strcat(npath, "/");
+ strcat(npath, frameworkPartialPath);
+ handler(npath, innerStop);
+ if ( innerStop )
+ stop = innerStop;
+ });
+
+ }
+ else {
+ const char* libraryLeafName = getLibraryLeafName(initialPath);
+ const size_t libraryLeafNameLen = strlen(libraryLeafName);
+ // look at each DYLD_FALLBACK_LIBRARY_PATH directory
+ forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
+ char libpath[strlen(dir)+libraryLeafNameLen+8];
+ strcpy(libpath, dir);
+ strcat(libpath, "/");
+ strcat(libpath, libraryLeafName);
+ handler(libpath, innerStop);
+ if ( innerStop )
+ stop = innerStop;
+ });
+ }
+}
+
+
+//
+// Find framework path
+//
+// /path/foo.framework/foo => foo.framework/foo
+// /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo
+// /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar
+// /path/foo.framework/Libraries/bar.dylb => NULL
+// /path/foo.framework/bar => NULL
+//
+// Returns nullptr if not a framework path
+//
+const char* PathOverrides::getFrameworkPartialPath(const char* path) const
+{
+ const char* dirDot = strrstr(path, ".framework/");
+ if ( dirDot != nullptr ) {
+ const char* dirStart = dirDot;
+ for ( ; dirStart >= path; --dirStart) {
+ if ( (*dirStart == '/') || (dirStart == path) ) {
+ const char* frameworkStart = &dirStart[1];
+ if ( dirStart == path )
+ --frameworkStart;
+ size_t len = dirDot - frameworkStart;
+ char framework[len+1];
+ strncpy(framework, frameworkStart, len);
+ framework[len] = '\0';
+ const char* leaf = strrchr(path, '/');
+ if ( leaf != nullptr ) {
+ if ( strcmp(framework, &leaf[1]) == 0 ) {
+ return frameworkStart;
+ }
+ if ( _imageSuffix != nullptr ) {
+ // some debug frameworks have install names that end in _debug
+ if ( strncmp(framework, &leaf[1], len) == 0 ) {
+ if ( strcmp( _imageSuffix, &leaf[len+1]) == 0 )
+ return frameworkStart;
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+const char* PathOverrides::getLibraryLeafName(const char* path)
+{
+ const char* start = strrchr(path, '/');
+ if ( start != nullptr )
+ return &start[1];
+ else
+ return path;
+}
+
+} // namespace dyld3
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_PATH_OVERRIDES_H__
+#define __DYLD_PATH_OVERRIDES_H__
+
+#include <stdint.h>
+
+#if !DYLD_IN_PROCESS
+#include <vector>
+#include <string>
+#endif
+
+#include "Logging.h"
+#include "MachOParser.h"
+
+
+namespace dyld3 {
+
+class VIS_HIDDEN PathOverrides
+{
+public:
+#if !BUILDING_LIBDYLD
+ // libdyld is never unloaded
+ ~PathOverrides();
+#endif
+
+#if DYLD_IN_PROCESS
+ void setEnvVars(const char* envp[]);
+ void forEachPathVariant(const char* initialPath, void (^handler)(const char* possiblePath, bool& stop)) const;
+#else
+ PathOverrides(const std::vector<std::string>& env);
+ void forEachPathVariant(const char* initialPath, Platform platform, void (^handler)(const char* possiblePath, bool& stop)) const;
+#endif
+
+ uint32_t envVarCount() const;
+ void forEachEnvVar(void (^handler)(const char* envVar)) const;
+ void forEachInsertedDylib(void (^handler)(const char* dylibPath)) const;
+
+private:
+ void forEachInColonList(const char* list, void (^callback)(const char* path));
+ const char** parseColonListIntoArray(const char* list);
+ void freeArray(const char** array);
+ void addEnvVar(const char* keyEqualsValue);
+ const char* getFrameworkPartialPath(const char* path) const;
+ static const char* getLibraryLeafName(const char* path);
+ void handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const;
+ void handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const;
+ void forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const;
+ void forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const;
+
+ const char** _dylibPathOverrides = nullptr;
+ const char** _frameworkPathOverrides = nullptr;
+ const char** _dylibPathFallbacks = nullptr;
+ const char** _frameworkPathFallbacks = nullptr;
+ const char** _insertedDylibs = nullptr;
+ const char* _imageSuffix = nullptr;
+ const char* _rootPath = nullptr; // simulator only
+};
+
+#if BUILDING_LIBDYLD
+extern PathOverrides gPathOverrides;
+#endif
+
+
+} // namespace dyld3
+
+#endif // __DYLD_PATH_OVERRIDES_H__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/ldsyms.h>
+#include <mach/shared_region.h>
+#include <mach/mach.h>
+#include <Availability.h>
+#include <TargetConditionals.h>
+
+#include "dyld_cache_format.h"
+#include "SharedCacheRuntime.h"
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "Loading.h"
+
+#define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024
+
+// should be in mach/shared_region.h
+extern "C" int __shared_region_check_np(uint64_t* startaddress);
+extern "C" int __shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], long slide, const dyld_cache_slide_info2* slideInfo, size_t slideInfoSize);
+
+
+namespace dyld {
+ extern int my_stat(const char* path, struct stat* buf);
+ extern int my_open(const char* path, int flag, int other);
+ extern void log(const char*, ...);
+}
+
+
+namespace dyld3 {
+
+
+struct CacheInfo
+{
+ int fd;
+ shared_file_mapping_np mappings[3];
+ uint64_t slideInfoAddressUnslid;
+ size_t slideInfoSize;
+ uint64_t cachedDylibsGroupUnslid;
+ uint64_t sharedRegionStart;
+ uint64_t sharedRegionSize;
+ uint64_t maxSlide;
+};
+
+
+
+
+#if __i386__
+ #define ARCH_NAME "i386"
+ #define ARCH_CACHE_MAGIC "dyld_v1 i386"
+#elif __x86_64__
+ #define ARCH_NAME "x86_64"
+ #define ARCH_CACHE_MAGIC "dyld_v1 x86_64"
+ #define ARCH_NAME_H "x86_64h"
+ #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h"
+#elif __ARM_ARCH_7K__
+ #define ARCH_NAME "armv7k"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv7k"
+#elif __ARM_ARCH_7A__
+ #define ARCH_NAME "armv7"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv7"
+#elif __ARM_ARCH_7S__
+ #define ARCH_NAME "armv7s"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv7s"
+#elif __arm64e__
+ #define ARCH_NAME "arm64e"
+ #define ARCH_CACHE_MAGIC "dyld_v1 arm64e"
+#elif __arm64__
+ #define ARCH_NAME "arm64"
+ #define ARCH_CACHE_MAGIC "dyld_v1 arm64"
+#endif
+
+
+
+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 getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
+{
+ // set cache dir
+ if ( options.cacheDirOverride != nullptr ) {
+ strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize);
+ }
+ else {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
+#else
+ strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
+#endif
+ }
+
+ // append file component of cache file
+ if ( pathBuffer[strlen(pathBuffer)-1] != '/' )
+ strlcat(pathBuffer, "/", pathBufferSize);
+#if __x86_64__ && !__IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( options.useHaswell ) {
+ size_t len = strlen(pathBuffer);
+ struct stat haswellStatBuf;
+ strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, pathBufferSize);
+ if ( dyld::my_stat(pathBuffer, &haswellStatBuf) == 0 )
+ return;
+ // no haswell cache file, use regular x86_64 cache
+ pathBuffer[len] = '\0';
+ }
+#endif
+ strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize);
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ // use .development cache if it exists
+ struct stat enableStatBuf;
+ struct stat devCacheStatBuf;
+ struct stat optCacheStatBuf;
+ bool enableFileExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0);
+ bool devCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0);
+ bool optCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &optCacheStatBuf) == 0);
+ if ( (enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists )
+ strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+#endif
+
+}
+
+
+int openSharedCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+ getCachePath(options, sizeof(results->path), results->path);
+ return dyld::my_open(results->path, O_RDONLY, 0);
+}
+
+static bool validMagic(const SharedCacheOptions& options, const DyldSharedCache* cache)
+{
+ if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC) == 0 )
+ return true;
+
+#if __x86_64__
+ if ( options.useHaswell ) {
+ if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC_H) == 0 )
+ return true;
+ }
+#endif
+ return false;
+}
+
+
+static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCache* cache)
+{
+ // grandfather in old cache that does not have platform in header
+ if ( cache->header.mappingOffset < 0xE0 )
+ return true;
+
+ if ( cache->header.platform != (uint32_t)MachOParser::currentPlatform() )
+ return false;
+
+#if TARGET_IPHONE_SIMULATOR
+ if ( cache->header.simulator == 0 )
+ return false;
+#else
+ if ( cache->header.simulator != 0 )
+ return false;
+#endif
+
+ return true;
+}
+
+
+static void verboseSharedCacheMappings(const shared_file_mapping_np mappings[3])
+{
+ for (int i=0; i < 3; ++i) {
+ dyld::log(" 0x%08llX->0x%08llX init=%x, max=%x %s%s%s\n",
+ mappings[i].sfm_address, mappings[i].sfm_address+mappings[i].sfm_size-1,
+ mappings[i].sfm_init_prot, mappings[i].sfm_init_prot,
+ ((mappings[i].sfm_init_prot & VM_PROT_READ) ? "read " : ""),
+ ((mappings[i].sfm_init_prot & VM_PROT_WRITE) ? "write " : ""),
+ ((mappings[i].sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""));
+ }
+}
+
+static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results, CacheInfo* info)
+{
+ // find and open shared cache file
+ int fd = openSharedCacheFile(options, results);
+ if ( fd == -1 ) {
+ results->errorMessage = "shared cache file cannot be opened";
+ return false;
+ }
+ struct stat cacheStatBuf;
+ if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) {
+ results->errorMessage = "shared cache file cannot be stat()ed";
+ ::close(fd);
+ return false;
+ }
+ size_t cacheFileLength = (size_t)(cacheStatBuf.st_size);
+
+ // sanity check header and mappings
+ uint8_t firstPage[0x4000];
+ if ( ::pread(fd, firstPage, sizeof(firstPage), 0) != sizeof(firstPage) ) {
+ results->errorMessage = "shared cache header could not be read";
+ ::close(fd);
+ return false;
+ }
+ const DyldSharedCache* cache = (DyldSharedCache*)firstPage;
+ if ( !validMagic(options, cache) ) {
+ results->errorMessage = "shared cache file has wrong magic";
+ ::close(fd);
+ return false;
+ }
+ if ( !validPlatform(options, cache) ) {
+ results->errorMessage = "shared cache file is for a different platform";
+ ::close(fd);
+ return false;
+ }
+ const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset];
+ if ( (cache->header.mappingCount != 3)
+ || (cache->header.mappingOffset > 0x120)
+ || (fileMappings[0].fileOffset != 0)
+ || ((fileMappings[0].address + fileMappings[0].size) > fileMappings[1].address)
+ || ((fileMappings[1].address + fileMappings[1].size) > fileMappings[2].address)
+ || ((fileMappings[0].fileOffset + fileMappings[0].size) != fileMappings[1].fileOffset)
+ || ((fileMappings[1].fileOffset + fileMappings[1].size) != fileMappings[2].fileOffset)
+ || ((cache->header.codeSignatureOffset + cache->header.codeSignatureSize) != cacheFileLength)
+ || (fileMappings[0].maxProt != (VM_PROT_READ|VM_PROT_EXECUTE))
+ || (fileMappings[1].maxProt != (VM_PROT_READ|VM_PROT_WRITE))
+ || (fileMappings[2].maxProt != VM_PROT_READ) ) {
+ results->errorMessage = "shared cache file mappings are invalid";
+ ::close(fd);
+ return false;
+ }
+
+ if ( cache->header.mappingOffset >= 0xF8 ) {
+ if ( (fileMappings[0].address != cache->header.sharedRegionStart) || ((fileMappings[2].address + fileMappings[2].size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) {
+ results->errorMessage = "shared cache file mapping addressses invalid";
+ ::close(fd);
+ return false;
+ }
+ }
+ else {
+ if ( (fileMappings[0].address != SHARED_REGION_BASE) || ((fileMappings[2].address + fileMappings[2].size) > (SHARED_REGION_BASE+SHARED_REGION_SIZE)) ) {
+ results->errorMessage = "shared cache file mapping addressses invalid";
+ ::close(fd);
+ return false;
+ }
+ }
+
+ // register code signature of cache file
+ fsignatures_t siginfo;
+ siginfo.fs_file_start = 0; // cache always starts at beginning of file
+ siginfo.fs_blob_start = (void*)cache->header.codeSignatureOffset;
+ siginfo.fs_blob_size = (size_t)(cache->header.codeSignatureSize);
+ int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
+ if ( result == -1 ) {
+ results->errorMessage = "code signature registration for shared cache failed";
+ ::close(fd);
+ return false;
+ }
+
+ // <rdar://problem/23188073> validate code signature covers entire shared cache
+ uint64_t codeSignedLength = siginfo.fs_file_start;
+ if ( codeSignedLength < cache->header.codeSignatureOffset ) {
+ results->errorMessage = "code signature does not cover entire shared cache file";
+ ::close(fd);
+ return false;
+ }
+ void* mappedData = ::mmap(NULL, sizeof(firstPage), PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
+ if ( mappedData == MAP_FAILED ) {
+ results->errorMessage = "first page of shared cache not mmap()able";
+ ::close(fd);
+ return false;
+ }
+ if ( memcmp(mappedData, firstPage, sizeof(firstPage)) != 0 ) {
+ results->errorMessage = "first page of shared cache not mmap()able";
+ ::close(fd);
+ return false;
+ }
+ ::munmap(mappedData, sizeof(firstPage));
+
+ // fill out results
+ info->fd = fd;
+ for (int i=0; i < 3; ++i) {
+ info->mappings[i].sfm_address = fileMappings[i].address;
+ info->mappings[i].sfm_size = fileMappings[i].size;
+ info->mappings[i].sfm_file_offset = fileMappings[i].fileOffset;
+ info->mappings[i].sfm_max_prot = fileMappings[i].maxProt;
+ info->mappings[i].sfm_init_prot = fileMappings[i].initProt;
+ }
+ info->mappings[1].sfm_max_prot |= VM_PROT_SLIDE;
+ info->mappings[1].sfm_init_prot |= VM_PROT_SLIDE;
+ info->slideInfoAddressUnslid = fileMappings[2].address + cache->header.slideInfoOffset - fileMappings[2].fileOffset;
+ info->slideInfoSize = (long)cache->header.slideInfoSize;
+ if ( cache->header.mappingOffset > 0xD0 )
+ info->cachedDylibsGroupUnslid = cache->header.dylibsImageGroupAddr;
+ else
+ info->cachedDylibsGroupUnslid = 0;
+ if ( cache->header.mappingOffset >= 0xf8 ) {
+ info->sharedRegionStart = cache->header.sharedRegionStart;
+ info->sharedRegionSize = cache->header.sharedRegionSize;
+ info->maxSlide = cache->header.maxSlide;
+ }
+ else {
+ info->sharedRegionStart = SHARED_REGION_BASE;
+ info->sharedRegionSize = SHARED_REGION_SIZE;
+ info->maxSlide = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address);
+ }
+ return true;
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+ uint64_t cacheBaseAddress;
+#if __i386__
+ if ( syscall(294, &cacheBaseAddress) == 0 ) {
+#else
+ if ( __shared_region_check_np(&cacheBaseAddress) == 0 ) {
+#endif
+ const DyldSharedCache* existingCache = (DyldSharedCache*)cacheBaseAddress;
+ if ( validMagic(options, existingCache) ) {
+ const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)(cacheBaseAddress + existingCache->header.mappingOffset);
+ results->loadAddress = existingCache;
+ results->slide = (long)(cacheBaseAddress - fileMappings[0].address);
+ if ( (existingCache->header.mappingOffset > 0xD0) && (existingCache->header.dylibsImageGroupAddr != 0) )
+ results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(existingCache->header.dylibsImageGroupAddr + results->slide);
+ else
+ results->cachedDylibsGroup = nullptr;
+ // we don't know the path this cache was previously loaded from, assume default
+ getCachePath(options, sizeof(results->path), results->path);
+ if ( options.verbose ) {
+ const shared_file_mapping_np* const mappings = (shared_file_mapping_np*)(cacheBaseAddress + existingCache->header.mappingOffset);
+ dyld::log("re-using existing shared cache (%s):\n", results->path);
+ shared_file_mapping_np slidMappings[3];
+ for (int i=0; i < 3; ++i) {
+ slidMappings[i] = mappings[i];
+ slidMappings[i].sfm_address += results->slide;
+ }
+ verboseSharedCacheMappings(slidMappings);
+ }
+ }
+ else {
+ results->errorMessage = "existing shared cache in memory is not compatible";
+ }
+ return true;
+ }
+ return false;
+}
+
+static long pickCacheASLR(CacheInfo& info)
+{
+ // choose new random slide
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/20848977> change shared cache slide for 32-bit arm to always be 16k aligned
+ long slide = ((arc4random() % info.maxSlide) & (-16384));
+#else
+ long slide = ((arc4random() % info.maxSlide) & (-4096));
+#endif
+
+ // <rdar://problem/32031197> respect -disable_aslr boot-arg
+ if ( dyld3::loader::bootArgsContains("-disable_aslr") )
+ slide = 0;
+
+ // update mappings
+ for (uint32_t i=0; i < 3; ++i) {
+ info.mappings[i].sfm_address += slide;
+ }
+
+ return slide;
+}
+
+static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+ CacheInfo info;
+ if ( !preflightCacheFile(options, results, &info) )
+ return false;
+
+ const dyld_cache_slide_info2* slideInfo = nullptr;
+ if ( info.slideInfoSize != 0 ) {
+ results->slide = pickCacheASLR(info);
+ slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
+ }
+ if ( info.cachedDylibsGroupUnslid != 0 )
+ results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide);
+ else
+ results->cachedDylibsGroup = nullptr;
+
+ int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize);
+ ::close(info.fd);
+ if ( result == 0 ) {
+ results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+ }
+ else {
+ // could be another process beat us to it
+ if ( reuseExistingCache(options, results) )
+ return true;
+ // if cache does not exist, then really is an error
+ results->errorMessage = "syscall to map cache into shared region failed";
+ return false;
+ }
+
+ if ( options.verbose ) {
+ dyld::log("mapped dyld cache file system wide: %s\n", results->path);
+ verboseSharedCacheMappings(info.mappings);
+ }
+ return true;
+}
+#endif
+
+static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+ // open and validate cache file
+ CacheInfo info;
+ if ( !preflightCacheFile(options, results, &info) )
+ return false;
+
+ // compute ALSR slide
+ results->slide = 0;
+ const dyld_cache_slide_info2* slideInfo = nullptr;
+#if !TARGET_IPHONE_SIMULATOR // simulator caches do not support sliding
+ if ( info.slideInfoSize != 0 ) {
+ results->slide = pickCacheASLR(info);
+ slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
+ }
+#endif
+ results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+ if ( info.cachedDylibsGroupUnslid != 0 )
+ results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide);
+ else
+ results->cachedDylibsGroup = nullptr;
+
+ // remove the shared region sub-map
+ vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize);
+
+ // map cache just for this process with mmap()
+ for (int i=0; i < 3; ++i) {
+ void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sfm_address);
+ size_t size = (size_t)(info.mappings[i].sfm_size);
+ //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size);
+ int protection = 0;
+ if ( info.mappings[i].sfm_init_prot & VM_PROT_EXECUTE )
+ protection |= PROT_EXEC;
+ if ( info.mappings[i].sfm_init_prot & VM_PROT_READ )
+ protection |= PROT_READ;
+ if ( info.mappings[i].sfm_init_prot & VM_PROT_WRITE )
+ protection |= PROT_WRITE;
+ off_t offset = info.mappings[i].sfm_file_offset;
+ if ( ::mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, info.fd, offset) != mmapAddress ) {
+ // failed to map some chunk of this shared cache file
+ // clear shared region
+ vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize);
+ // return failure
+ results->loadAddress = nullptr;
+ results->cachedDylibsGroup = nullptr;
+ results->errorMessage = "could not mmap() part of dyld cache";
+ return false;
+ }
+ }
+
+ // update all __DATA pages with slide info
+ const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+ if ( slideInfoHeader != nullptr ) {
+ if ( slideInfoHeader->version != 2 ) {
+ results->errorMessage = "invalide slide info in cache file";
+ return false;
+ }
+ 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 = (uintptr_t)info.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 pInfo = page_extras[chainIndex];
+ uint16_t pageStartOffset = (pInfo & 0x3FFF)*4;
+ //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
+ rebaseChain(page, pageStartOffset, results->slide, slideInfo);
+ done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+ ++chainIndex;
+ }
+ }
+ else {
+ uint32_t pageOffset = pageEntry * 4;
+ //dyld::log(" start pageOffset=0x%03X\n", pageOffset);
+ rebaseChain(page, pageOffset, results->slide, slideInfo);
+ }
+ }
+ }
+
+ if ( options.verbose ) {
+ dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
+ verboseSharedCacheMappings(info.mappings);
+ }
+ return true;
+}
+
+
+
+bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+ results->loadAddress = 0;
+ results->slide = 0;
+ results->cachedDylibsGroup = nullptr;
+ results->errorMessage = nullptr;
+
+#if TARGET_IPHONE_SIMULATOR
+ // simulator only supports mmap()ing cache privately into process
+ return mapCachePrivate(options, results);
+#else
+ if ( options.forcePrivate ) {
+ // mmap cache into this process only
+ return mapCachePrivate(options, results);
+ }
+ else {
+ // fast path: when cache is already mapped into shared region
+ if ( reuseExistingCache(options, results) )
+ return (results->errorMessage != nullptr);
+
+ // slow path: this is first process to load cache
+ return mapCacheSystemWide(options, results);
+ }
+#endif
+}
+
+
+bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results)
+{
+ if ( loadInfo.loadAddress == nullptr )
+ return false;
+
+ // HACK: temp support for old caches
+ if ( (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) ) {
+ const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)loadInfo.loadAddress + loadInfo.loadAddress->header.imagesOffset);
+ const dyld_cache_image_info* const end = &start[loadInfo.loadAddress->header.imagesCount];
+ for (const dyld_cache_image_info* p = start; p != end; ++p) {
+ const char* aPath = (char*)loadInfo.loadAddress + p->pathFileOffset;
+ if ( strcmp(aPath, dylibPathToFind) == 0 ) {
+ results->mhInCache = (const mach_header*)(p->address+loadInfo.slide);
+ results->pathInCache = aPath;
+ results->slideInCache = loadInfo.slide;
+ results->imageData = nullptr;
+ return true;
+ }
+ }
+ return false;
+ }
+ // HACK: end
+
+ launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup);
+ uint32_t foundIndex;
+ const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // <rdar://problem/32740215> handle symlink to cached dylib
+ if ( imageData == nullptr ) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(dylibPathToFind, resolvedPath) != nullptr )
+ imageData = dylibsGroup.findImageByPath(resolvedPath, foundIndex);
+ }
+#endif
+ if ( imageData == nullptr )
+ return false;
+
+ launch_cache::Image image(imageData);
+ results->mhInCache = (const mach_header*)((uintptr_t)loadInfo.loadAddress + image.cacheOffset());
+ results->pathInCache = image.path();
+ results->slideInCache = loadInfo.slide;
+ results->imageData = imageData;
+ return true;
+}
+
+
+bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind)
+{
+ if ( (loadInfo.loadAddress == nullptr) || (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) )
+ return false;
+
+ launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup);
+ uint32_t foundIndex;
+ const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex);
+ return (imageData != nullptr);
+}
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_SHARED_CACHE_RUNTIME_H__
+#define __DYLD_SHARED_CACHE_RUNTIME_H__
+
+#include <string.h>
+#include <stdint.h>
+
+#include "DyldSharedCache.h"
+
+namespace dyld3 {
+
+struct SharedCacheOptions {
+ const char* cacheDirOverride;
+ bool forcePrivate;
+ bool useHaswell;
+ bool verbose;
+};
+
+struct SharedCacheLoadInfo {
+ const DyldSharedCache* loadAddress;
+ long slide;
+ const launch_cache::binary_format::ImageGroup* cachedDylibsGroup;
+ const char* errorMessage;
+ char path[256];
+};
+
+bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results);
+
+struct SharedCacheFindDylibResults {
+ const mach_header* mhInCache;
+ const char* pathInCache;
+ long slideInCache;
+ const launch_cache::binary_format::Image* imageData;
+};
+
+
+bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results);
+
+bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind);
+
+
+} // namespace dyld3
+
+#endif // __DYLD_SHARED_CACHE_RUNTIME_H__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <atomic>
+
+#include <assert.h>
+#include <mach/mach.h>
+
+#include "Tracing.h"
+
+namespace {
+VIS_HIDDEN
+static uint64_t elapsed(const time_value_t start, const time_value_t end) {
+ uint64_t duration;
+ duration = 1000000*(end.seconds - start.seconds);
+ duration += (end.microseconds - start.microseconds);
+ return duration;
+}
+}
+
+namespace dyld3 {
+
+VIS_HIDDEN
+void kdebug_trace_dyld_image(const uint32_t code,
+ const uuid_t* uuid_bytes,
+ const fsobj_id_t fsobjid,
+ const fsid_t fsid,
+ const mach_header* load_addr)
+{
+#if __LP64__
+ uint64_t *uuid = (uint64_t *)uuid_bytes[0];
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code), uuid[0],
+ uuid[1], (uint64_t)load_addr,
+ (uint64_t)fsid.val[0] | ((uint64_t)fsid.val[1] << 32));
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 1),
+ (uint64_t)fsobjid.fid_objno |
+ ((uint64_t)fsobjid.fid_generation << 32),
+ 0, 0, 0);
+#else /* __LP64__ */
+ uint32_t *uuid = (uint32_t *)uuid_bytes[0];
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 2), uuid[0],
+ uuid[1], uuid[2], uuid[3]);
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 3),
+ (uint32_t)load_addr, fsid.val[0], fsid.val[1],
+ fsobjid.fid_objno);
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 4),
+ fsobjid.fid_generation, 0, 0, 0);
+#endif /* __LP64__ */
+}
+
+VIS_HIDDEN
+void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2) {
+ if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code))) {
+ task_thread_times_info info;
+ mach_msg_type_number_t infoSize = sizeof(task_thread_times_info);
+ (void)task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&info, &infoSize);
+ uint64_t user_duration = elapsed({0,0}, info.user_time);
+ uint64_t system_duration = elapsed({0,0}, info.system_time);
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code), user_duration, system_duration, data1, data2);
+ }
+}
+
+static std::atomic<uint64_t> trace_pair_id(0);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)()) {
+ //FIXME: We should assert here, but it is verified on our current platforms
+ //Re-enabled when we move to C++17 and can use constexpr is_lock_always_free()
+ //assert(std::atomic<uint64_t>{}.is_lock_free());
+ if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code))) {
+ uint64_t current_trace_id = trace_pair_id++;
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_START, current_trace_id, 0, data1, data2);
+ block();
+ kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_END, current_trace_id, 0, data1, data2);
+ } else {
+ block();
+ }
+}
+
+void kdebug_trace_print(const uint32_t code, const char *string) {
+ if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code))) {
+ kdebug_trace_string(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code), 0, string);
+ }
+}
+};
--- /dev/null
+/*
+ * Copyright (c) 2017 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 Tracing_h
+#define Tracing_h
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+#include <System/sys/kdebug.h>
+
+#ifndef DBG_DYLD_SIGNPOST
+ #define DBG_DYLD_SIGNPOST (6)
+#endif
+
+#ifndef DBG_DYLD_TIMING
+ #define DBG_DYLD_TIMING (7)
+#endif
+
+#ifndef DBG_DYLD_PRINT
+ #define DBG_DYLD_PRINT (8)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_DYLD
+ #define DBG_DYLD_SIGNPOST_START_DYLD (0)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_MAIN
+ #define DBG_DYLD_SIGNPOST_START_MAIN (1)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_MAIN_DYLD2
+ #define DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 (2)
+#endif
+
+#ifndef DBG_DYLD_TIMING_STATIC_INITIALIZER
+ #define DBG_DYLD_TIMING_STATIC_INITIALIZER (0)
+#endif
+
+#ifndef DBG_DYLD_PRINT_GENERIC
+ #define DBG_DYLD_PRINT_GENERIC (0)
+#endif
+
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+VIS_HIDDEN
+void kdebug_trace_dyld_image(const uint32_t code,
+ const uuid_t* uuid_bytes,
+ const fsobj_id_t fsobjid,
+ const fsid_t fsid,
+ const mach_header* load_addr);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)());
+
+VIS_HIDDEN
+void kdebug_trace_print(const uint32_t code, const char *string);
+}
+
+#endif /* Tracing_h */
--- /dev/null
+/*
+ * Copyright (c) 2017 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>
+#include <string.h>
+#include <sandbox/private.h>
+#include <bootstrap.h>
+#include <mach/mach.h>
+#include <os/log.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <dispatch/dispatch.h>
+#include <dispatch/private.h>
+#include <bootstrap_priv.h> // for bootstrap_check_in()
+#include <CrashReporterClient.h>
+#include <libproc.h>
+#include <uuid/uuid.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "dyld_priv.h"
+#include "ImageProxy.h"
+#include "DyldSharedCache.h"
+#include "FileUtils.h"
+
+extern "C" {
+ #include "closuredProtocolServer.h"
+}
+
+
+static os_log_t sLog = os_log_create("com.apple.dyld.closured", "closured");
+
+static char sCrashMessageBuffer[1024];
+
+
+kern_return_t
+do_CreateClosure(
+ mach_port_t port,
+ task_t requestor,
+ vm_address_t buffer,
+ mach_msg_type_number_t bufferCnt,
+ vm_address_t* returnData,
+ mach_msg_type_number_t* returnDataCnt)
+{
+ dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt);
+ const char* imagePath = clsBuff.targetPath();
+ os_log(sLog, "request to build closure for %s\n", imagePath);
+
+ // set crash log message in case there is an assert during processing
+ strlcpy(sCrashMessageBuffer, "building closure for: ", sizeof(sCrashMessageBuffer));
+ strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer));
+ CRSetCrashLogMessage(sCrashMessageBuffer);
+
+ Diagnostics diag;
+ const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(diag, clsBuff, requestor);
+
+ os_log_info(sLog, "finished closure build, closure=%p\n", cls);
+ for (const std::string& message: diag.warnings())
+ os_log(sLog, "Image generated warning: %s\n", message.c_str());
+
+ if ( diag.noError() ) {
+ // on success return the closure binary in the "returnData" buffer
+ dyld3::ClosureBuffer result(cls);
+ *returnData = result.vmBuffer();
+ *returnDataCnt = result.vmBufferSize();
+ }
+ else {
+ // on failure return the error message in the "returnData" buffer
+ os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str());
+ dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+ *returnData = err.vmBuffer();
+ *returnDataCnt = err.vmBufferSize();
+ }
+
+ CRSetCrashLogMessage(nullptr);
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+do_CreateImageGroup(
+ mach_port_t port,
+ task_t requestor,
+ vm_address_t buffer,
+ mach_msg_type_number_t bufferCnt,
+ vm_address_t* returnData,
+ mach_msg_type_number_t* returnDataCnt)
+{
+ dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt);
+ const char* imagePath = clsBuff.targetPath();
+ int requestorPid;
+ char requestorName[MAXPATHLEN];
+ if ( pid_for_task(requestor, &requestorPid) == 0 ) {
+ int nameLen = proc_name(requestorPid, requestorName, sizeof(requestorName));
+ if ( nameLen <= 0 )
+ strcpy(requestorName, "???");
+ os_log(sLog, "request from %d (%s) to build dlopen ImageGroup for %s\n", requestorPid, requestorName, imagePath);
+ }
+
+ // set crash log message in case there is an assert during processing
+ strlcpy(sCrashMessageBuffer, "building ImageGroup for dlopen(", sizeof(sCrashMessageBuffer));
+ strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer));
+ strlcat(sCrashMessageBuffer, ") requested by ", sizeof(sCrashMessageBuffer));
+ strlcat(sCrashMessageBuffer, requestorName, sizeof(sCrashMessageBuffer));
+ CRSetCrashLogMessage(sCrashMessageBuffer);
+
+ uuid_string_t uuidStr;
+ dyld3::ClosureBuffer::CacheIdent cacheIdent = clsBuff.cacheIndent();
+ uuid_unparse(cacheIdent.cacheUUID, uuidStr);
+ os_log_info(sLog, "findDyldCache(): cache addr=0x%llX, size=0x%0llX, uuid = %s\n", cacheIdent.cacheAddress, cacheIdent.cacheMappedSize, uuidStr);
+
+ Diagnostics diag;
+ const dyld3::launch_cache::binary_format::ImageGroup* imageGroup = dyld3::ImageProxyGroup::makeDlopenGroup(diag, clsBuff, requestor, {""});
+
+ os_log(sLog, "finished ImageGroup build, imageGroup=%p\n", imageGroup);
+ for (const std::string& message: diag.warnings()) {
+ os_log(sLog, "Image generated warning: %s\n", message.c_str());
+ }
+
+ // delete incoming out-of-line data
+ vm_deallocate(mach_task_self(), buffer, bufferCnt);
+
+ if ( diag.noError() ) {
+ // on success return the ImageGroup binary in the "returnData" buffer
+ dyld3::ClosureBuffer result(imageGroup);
+ os_log_info(sLog, "returning closure buffer: 0x%lX, size=0x%X\n", (long)result.vmBuffer(), result.vmBufferSize());
+ *returnData = result.vmBuffer();
+ *returnDataCnt = result.vmBufferSize();
+ free((void*)imageGroup);
+ }
+ else {
+ // on failure return the error message in the "returnData" buffer
+ os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str());
+ dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+ *returnData = err.vmBuffer();
+ *returnDataCnt = err.vmBufferSize();
+ }
+
+ CRSetCrashLogMessage(nullptr);
+ return KERN_SUCCESS;
+}
+
+
+
+
+// /usr/libexec/closured -create_closure /Applications/TextEdit.app -pipefd 4 -env DYLD_FOO=1 -cache_uuid C153F90A-69F2-323E-AC9F-2BBAE48ABAF7
+int runAsTool(int argc, const char* argv[])
+{
+ const char* progPath = nullptr;
+ int pipeNum = -1;
+ bool verbose = false;
+ std::vector<std::string> dyldEnvVars;
+
+ dyld3::ClosureBuffer::CacheIdent cacheIdent;
+ bzero(&cacheIdent, sizeof(cacheIdent));
+
+ // set crash log message in case there is an assert during processing
+ sCrashMessageBuffer[0] = '\0';
+ for (int i=0; i < argc; ++i) {
+ strlcat(sCrashMessageBuffer, argv[i], sizeof(sCrashMessageBuffer));
+ strlcat(sCrashMessageBuffer, " ", sizeof(sCrashMessageBuffer));
+ }
+ CRSetCrashLogMessage(sCrashMessageBuffer);
+
+ for (int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( strcmp(arg, "-create_closure") == 0 ) {
+ progPath = argv[++i];
+ if ( progPath == nullptr ) {
+ fprintf(stderr, "-create_closure option requires a path to follow\n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-cache_uuid") == 0 ) {
+ const char* uuidStr = argv[++i];
+ if ( uuidStr == nullptr ) {
+ fprintf(stderr, "-cache_uuid option requires a path to follow\n");
+ return 1;
+ }
+ uuid_parse(uuidStr, cacheIdent.cacheUUID);
+ }
+ else if ( strcmp(arg, "-cache_address") == 0 ) {
+ const char* cacheAddr = argv[++i];
+ if ( cacheAddr == nullptr ) {
+ fprintf(stderr, "-cache_address option requires a path to follow\n");
+ return 1;
+ }
+ char *end;
+ cacheIdent.cacheAddress = strtol(cacheAddr, &end, 0);
+ }
+ else if ( strcmp(arg, "-cache_size") == 0 ) {
+ const char* cacheSize = argv[++i];
+ if ( cacheSize == nullptr ) {
+ fprintf(stderr, "-cache_size option requires a path to follow\n");
+ return 1;
+ }
+ char *end;
+ cacheIdent.cacheMappedSize = strtol(cacheSize, &end, 0);
+ }
+ else if ( strcmp(arg, "-pipefd") == 0 ) {
+ const char* numStr = argv[++i];
+ if ( numStr == nullptr ) {
+ fprintf(stderr, "-pipefd option requires an file descriptor number to follow\n");
+ return 1;
+ }
+ char *end;
+ pipeNum = (int)strtol(numStr, &end, 0);
+ if ( (pipeNum == 0) && (errno != 0) ) {
+ fprintf(stderr, "bad value (%s) for -pipefd option %d\n", numStr, pipeNum);
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-env") == 0 ) {
+ const char* var = argv[++i];
+ if ( var == nullptr ) {
+ fprintf(stderr, "-env option requires a following VAR=XXX\n");
+ return 1;
+ }
+ dyldEnvVars.push_back(var);
+ }
+ else {
+ fprintf(stderr, "unknown option: %s\n", arg);
+ return 1;
+ }
+ }
+ if ( progPath == nullptr ) {
+ fprintf(stderr, "missing required -create_closure option\n");
+ return 1;
+ }
+ if ( pipeNum == -1 ) {
+ fprintf(stderr, "missing required -pipefd option\n");
+ return 1;
+ }
+
+ if ( verbose ) {
+ fprintf(stderr, "closured: runAsTool()\n");
+ for (int i=1; i < argc; ++i)
+ fprintf(stderr, " argv[%d] = %s\n", i, argv[i]);
+ }
+
+ os_log(sLog, "fork/exec request to build closure for %s\n", progPath);
+ SocketBasedClousureHeader header;
+
+ // find dyld cache for requested arch
+ size_t currentCacheSize;
+ const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(¤tCacheSize);
+ if ( currentCache == nullptr ) {
+ os_log_error(sLog, "closured is running without a dyld cache\n");
+ return 1;
+ }
+ uuid_t currentCacheUUID;
+ currentCache->getUUID(currentCacheUUID);
+ if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) != 0 ) {
+ const char* errorString = "closured is running with a different dyld cache than client";
+ os_log_error(sLog, "%s\n", errorString);
+ header.success = 0;
+ header.length = (int)strlen(errorString) + 1;
+ write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+ write(pipeNum, errorString, header.length);
+ close(pipeNum);
+ return 0;
+ }
+ dyld3::DyldCacheParser cacheParser(currentCache, false);
+
+ Diagnostics diag;
+ os_log_info(sLog, "starting closure build\n");
+ const dyld3::launch_cache::BinaryClosureData* cls = dyld3::ImageProxyGroup::makeClosure(diag, cacheParser, progPath, false, {""}, dyldEnvVars);
+ os_log_info(sLog, "finished closure build, cls=%p\n", cls);
+ if ( diag.noError() ) {
+ // on success write the closure binary after the header to the socket
+ dyld3::launch_cache::Closure closure(cls);
+ os_log(sLog, "returning closure, size=%lu\n", closure.size());
+ header.success = 1;
+ header.length = (uint32_t)closure.size();
+ write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+ write(pipeNum, cls, closure.size());
+ }
+ else {
+ // on failure write the error message after the header to the socket
+ const std::string& message = diag.errorMessage();
+ os_log_error(sLog, "closure could not be created: %s\n", message.c_str());
+ header.success = 0;
+ header.length = (uint32_t)message.size() + 1;
+ write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+ write(pipeNum, message.c_str(), header.length);
+ }
+
+ close(pipeNum);
+
+ return 0;
+}
+
+
+union MaxMsgSize {
+ union __RequestUnion__do_closured_subsystem req;
+ union __ReplyUnion__do_closured_subsystem rep;
+};
+
+int main(int argc, const char* argv[])
+{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // establish sandbox around process
+ char* errMsg = nullptr;
+ if ( sandbox_init_with_parameters("com.apple.dyld.closured", SANDBOX_NAMED, nullptr, &errMsg) != 0 ) {
+ os_log_error(sLog, "Failed to enter sandbox: %{public}s", errMsg);
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ if ( argc != 1 )
+ return runAsTool(argc, argv);\
+
+ mach_port_t serverPort = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_check_in(bootstrap_port, CLOSURED_SERVICE_NAME, &serverPort);
+ if (kr != KERN_SUCCESS)
+ exit(-1);
+
+ dispatch_source_t machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, serverPort, 0, dispatch_get_main_queue());
+ if (machSource == nullptr)
+ exit(-1);
+
+ dispatch_source_set_event_handler(machSource, ^{
+ dispatch_mig_server(machSource, sizeof(union MaxMsgSize), closured_server);
+ });
+ dispatch_source_set_cancel_handler(machSource, ^{
+ mach_port_t port = (mach_port_t)dispatch_source_get_handle(machSource);
+ kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
+ if (kr != KERN_SUCCESS)
+ exit(-1);
+ });
+ dispatch_resume(machSource);
+ dispatch_main();
+
+ return 0;
+}
+
--- /dev/null
+
+
+#include <mach/mach_types.defs>
+#include <mach/std_types.defs>
+
+import "closuredtypes.h";
+
+subsystem closured 6000;
+
+userprefix closured_; // Routine prefixes for user access
+serverprefix do_; // Routine prefixes for internal server access
+
+type OutOfLineBuffer_t = ^array[] of MACH_MSG_TYPE_BYTE ctype: vm_address_t;
+
+// used at launch
+routine CreateClosure (
+ port : mach_port_t;
+ in requestor : task_t;
+ in buffer : OutOfLineBuffer_t;
+ out returnData : OutOfLineBuffer_t, dealloc
+);
+
+// used in dlopen()cl
+routine CreateImageGroup (
+ port : mach_port_t;
+ in requestor : task_t;
+ in buffer : OutOfLineBuffer_t;
+ out returnData : OutOfLineBuffer_t, dealloc
+);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>seatbelt-profiles</key>
+ <array>
+ <string>closured</string>
+ </array>
+ <key>platform-application</key>
+ <true/>
+ </dict>
+</plist>
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <stdbool.h>
+
+#undef __MigTypeCheck
+#undef USING_VOUCHERS
+
+
+typedef void* ClosureBufferPtr;
+typedef void* ClosureBufferConstPtr;
+
+struct SocketBasedClousureHeader
+{
+ uint32_t success; // 1 => rest of buffer is closure, 0 => rest of buffer is error string
+ uint32_t length;
+};
+
+#define CLOSURED_SERVICE_NAME "com.apple.dyld.closured"
+
+#define mig_external __private_extern__
+
--- /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>ProcessType</key>
+ <string>Adaptive</string>
+ <key>EnableTransactions</key>
+ <true/>
+ <key>EnablePressuredExit</key>
+ <true/>
+ <key>Label</key>
+ <string>com.apple.dyld.closured</string>
+ <key>MachServices</key>
+ <dict>
+ <key>com.apple.dyld.closured</key>
+ <true/>
+ </dict>
+ <key>TimeOut</key>
+ <integer>60</integer>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/libexec/closured</string>
+ </array>
+</dict>
+</plist>
--- /dev/null
+;;; Copyright (c) 2017 Apple Inc. All Rights reserved.
+;;;
+;;; WARNING: The sandbox rules in this file currently constitute
+;;; Apple System Private Interface and are subject to change at any time and
+;;; without notice.
+;;;
+(version 1)
+
+(deny default)
+(deny file-map-executable iokit-get-properties process-info* nvram*)
+(deny dynamic-code-generation)
+
+(import "system.sb")
+
+;; For reading dylibs
+(allow file-read*)
+
+;; For resolving symlinks, realpath(3), and equivalents.
+(allow file-read-metadata)
+
+;; for logging name of client
+(allow process-info-pidinfo)
--- /dev/null
+/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
+/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
+/System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox
+/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools
+/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit
+/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore
+
--- /dev/null
+
+namespace dyld3 {
+
+struct ClosureBuffer { int x; };
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
+{
+ return ClosureBuffer();
+}
+
+
+} // namespace dyld3
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <stdarg.h>
+
+#include "dyld_priv.h"
+#include "libdyldEntryVector.h"
+#include "AllImages.h"
+#include "Logging.h"
+#include "PathOverrides.h"
+#include "LaunchCacheFormat.h"
+
+extern "C" void start();
+
+
+VIS_HIDDEN const char** appleParams;
+
+extern bool gUseDyld3;
+
+namespace dyld3 {
+
+
+AllImages::ProgramVars sVars;
+static void (*sChildForkFunction)();
+
+static const char* leafName(const char* argv0)
+{
+ if ( argv0 == nullptr )
+ return "";
+
+ if ( const char* lastSlash = strrchr(argv0, '/') )
+ return lastSlash+1;
+ else
+ return argv0;
+}
+
+static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+ NXArgc = argc;
+ NXArgv = argv;
+ environ = (char**)envp;
+ appleParams = apple;
+ __progname = leafName(argv[0]);
+
+ sVars.mh = mainMH;
+ sVars.NXArgcPtr = &NXArgc;
+ sVars.NXArgvPtr = &NXArgv;
+ sVars.environPtr = (const char***)&environ;
+ sVars.__prognamePtr = &__progname;
+ gAllImages.setProgramVars(&sVars);
+
+ gUseDyld3 = true;
+
+ setLoggingFromEnvs(envp);
+}
+
+static void entry_setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)) )
+{
+ setHaltFunction(func);
+}
+
+static void entry_setLogFunction(void (*logFunction)(const char* format, va_list list))
+{
+ setLoggingFunction(logFunction);
+}
+
+static void entry_setOldAllImageInfo(dyld_all_image_infos* old)
+{
+ gAllImages.setOldAllImageInfo(old);
+}
+
+static void entry_setInitialImageList(const launch_cache::binary_format::Closure* closure,
+ const void* dyldCacheLoadAddress, const char* dyldCachePath,
+ const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages,
+ const mach_header* libSystemMH, const launch_cache::binary_format::Image* libSystemImage)
+{
+ gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages);
+ gAllImages.applyInterposingToDyldCache(closure, initialImages);
+
+ const char* mainPath = _simple_getenv(appleParams, "executable_path");
+ if ( (mainPath != nullptr) && (mainPath[0] == '/') )
+ gAllImages.setMainPath(mainPath);
+
+ // ok to call before malloc is ready because 4 slots are reserved.
+ gAllImages.setInitialGroups();
+
+ // run initializer for libSytem.B.dylib
+ // this calls back into _dyld_initializer which calls gAllIimages.addImages()
+ gAllImages.runLibSystemInitializer(libSystemMH, libSystemImage);
+
+ // now that malloc is available, parse DYLD_ env vars
+ gPathOverrides.setEnvVars((const char**)environ);
+}
+
+static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress)
+{
+ gAllImages.runInitialzersBottomUp(mainExecutableImageLoadAddress);
+ gAllImages.notifyMonitorMain();
+}
+
+static void entry_setChildForkFunction(void (*func)() )
+{
+ sChildForkFunction = func;
+}
+
+const LibDyldEntryVector entryVectorForDyld = {
+ LibDyldEntryVector::kCurrentVectorVersion,
+ launch_cache::binary_format::kFormatVersion,
+ &entry_setVars,
+ &entry_setHaltFunction,
+ &entry_setOldAllImageInfo,
+ &entry_setInitialImageList,
+ &entry_runInitialzersBottomUp,
+ &start,
+ &entry_setChildForkFunction,
+ &entry_setLogFunction,
+};
+
+VIS_HIDDEN void _dyld_fork_child()
+{
+ (*sChildForkFunction)();
+}
+
+
+} // namespace dyld3
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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_ENTRY_VECTOR_H__
+#define __DYLD_ENTRY_VECTOR_H__
+
+#include <mach-o/loader.h>
+
+#include "LaunchCache.h"
+#include "Loading.h"
+
+struct dyld_all_image_infos;
+
+namespace dyld3 {
+
+struct LibDyldEntryVector
+{
+ enum { kCurrentVectorVersion = 4 };
+
+ uint32_t vectorVersion; // should be kCurrentVectorVersion
+ uint32_t binaryFormatVersion; // should be launch_cache::binary_format::kFormatVersion
+ void (*setVars)(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[]);
+ void (*setHaltFunction)(void (*func)(const char* message) __attribute__((noreturn)) );
+ void (*setOldAllImageInfo)(dyld_all_image_infos*);
+ void (*setInitialImageList)(const launch_cache::BinaryClosureData* closure,
+ const void* dyldCacheLoadAddress, const char* dyldCachePath,
+ const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages,
+ const mach_header* libSystemMH, const launch_cache::BinaryImageData* libSystemImage);
+ void (*runInitialzersBottomUp)(const mach_header* topImageLoadAddress);
+ void (*startFunc)();
+ // added in version 3
+ void (*setChildForkFunction)(void (*func)());
+ // added in version 4
+ void (*setLogFunction)(void (*logFunction)(const char* format, va_list list));
+};
+
+extern const LibDyldEntryVector entryVectorForDyld;
+
+} // namespace dyld3
+
+
+#endif // __DYLD_ENTRY_VECTOR_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 <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 <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "CacheBuilder.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "Trie.hpp"
+#include "MachOFileAbstraction.hpp"
+
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+namespace {
+
+template <typename P>
+class Adjustor {
+public:
+ Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
+ 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;
+
+ DyldSharedCache* _cacheBuffer;
+ macho_header<P>* _mh;
+ Diagnostics& _diagnostics;
+ 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> _segSlides;
+ std::vector<macho_segment_command<P>*> _segCmds;
+ const std::vector<CacheBuilder::SegmentMappingInfo>& _mappingInfo;
+};
+
+template <typename P>
+Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag)
+ : _cacheBuffer(cacheBuffer), _mh(mh), _diagnostics(diag), _mappingInfo(mappingInfo)
+{
+ assert((mh->magic() == MH_MAGIC) || (mh->magic() == MH_MAGIC_64));
+ 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(_mappingInfo[segIndex].dstCacheAddress - segCmd->vmaddr());
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditAdjust = _mappingInfo[segIndex].dstCacheOffset - 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);
+ }
+ else {
+ _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _installName);
+ }
+}
+
+template <typename P>
+void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR)
+{
+ if ( _diagnostics.hasError() )
+ return;
+ if ( _splitSegInfoV2 ) {
+ adjustReferencesUsingInfoV2(pointersForASLR);
+ }
+ else {
+ adjustDataPointers(pointersForASLR);
+ adjustCode();
+ }
+ if ( _diagnostics.hasError() )
+ return;
+ adjustSymbolTable();
+ if ( _diagnostics.hasError() )
+ return;
+ 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);
+ }
+ _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _installName);
+ return 0;
+}
+
+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 ) {
+ _diagnostics.error("LINKEDIT overflow in %s", _installName);
+ return;
+ }
+
+ uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheOffset;
+ 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)(_mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff());
+ segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheAddress);
+ segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize);
+ segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheOffset);
+ segCmd->set_filesize(_mappingInfo[segIndex].copySegmentSize);
+ 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:
+ _diagnostics.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 + _mappingInfo[segIndex].dstCacheOffset + 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:
+ _diagnostics.error("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 = 0;
+ uint32_t value32;
+ uint32_t* mappedAddr32 = 0;
+ 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) ) {
+ _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName);
+ return;
+ }
+ 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) ) {
+ _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+ return;
+ }
+ 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) ) {
+ _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+ return;
+ }
+ 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 ) {
+ _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName);
+ return;
+ }
+ P::E::set32(*mappedAddr32, (uint32_t)value64);
+ 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) ) {
+ _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName);
+ return;
+ }
+ 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 ) {
+ _diagnostics.error("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ return;
+ }
+ if ( encodedAddend*16 >= 4096 ) {
+ _diagnostics.error("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 ) {
+ _diagnostics.error("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ return;
+ }
+ if ( encodedAddend*2 >= 4096 ) {
+ _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ return;
+ }
+ newAddend = (encodedAddend + offsetAdjust/2) % 2048;
+ break;
+ case 0x80000000:
+ if ( offsetAdjust & 3 ) {
+ _diagnostics.error("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ return;
+ }
+ if ( encodedAddend*4 >= 4096 ) {
+ _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ return;
+ }
+ newAddend = (encodedAddend + offsetAdjust/4) % 1024;
+ break;
+ case 0xC0000000:
+ if ( offsetAdjust & 7 ) {
+ _diagnostics.error("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+ return;
+ }
+ if ( encodedAddend*8 >= 4096 ) {
+ _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+ return;
+ }
+ 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 ) {
+ _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName);
+ return;
+ }
+ 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
+ _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName);
+ return;
+ }
+ 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 {
+ _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName);
+ return;
+ }
+ P::E::set32(*lastMappedAddr32, instruction1);
+ P::E::set32(*mappedAddr32, instruction2);
+ kind = 0;
+ }
+ else {
+ _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName);
+ return;
+ }
+ }
+ 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 {
+ _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName);
+ return;
+ }
+ P::E::set32(*lastMappedAddr32, instruction1);
+ P::E::set32(*mappedAddr32, instruction2);
+ kind = 0;
+ }
+ else {
+ _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName);
+ return;
+ }
+ }
+ 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:
+ _diagnostics.error("unknown split seg kind=%d in %s", kind, _installName);
+ return;
+ }
+ 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 ) {
+ _diagnostics.error("malformed split seg info in %s", _installName);
+ return;
+ }
+ // 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 + _mappingInfo[0].dstCacheOffset);
+ sectionSlides.push_back(_segSlides[0]);
+ sectionNewAddress.push_back(_mappingInfo[0].dstCacheAddress);
+ // 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 + _mappingInfo[segmentIndex].dstCacheOffset + sect->addr() - segCmd->vmaddr());
+ sectionSlides.push_back(_segSlides[segmentIndex]);
+ sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheAddress + 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 ) {
+ _diagnostics.error("bad kind value (%llu) in %s", kind, _installName);
+ return;
+ }
+ 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);
+ if ( _diagnostics.hasError() )
+ return;
+ }
+ }
+ }
+ }
+
+}
+
+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:
+ _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _installName);
+ done = true;
+ break;
+ }
+ }
+}
+
+
+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 = _mappingInfo[0].dstCacheOffset;
+ 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) ) {
+ _diagnostics.error("malformed exports trie in %s", _installName);
+ return;
+ }
+
+ 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 adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+ if ( is64 ) {
+ Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)mhInCache, mappingInfo, diag);
+ adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
+ }
+ else {
+ Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)mhInCache, mappingInfo, diag);
+ adjustor32.adjustImageForNewSegmentLocations(pointersForASLR);
+ }
+}
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 BuilderUtils_h
+#define BuilderUtils_h
+
+dispatch_group_t buildGroup();
+void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot);
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash);
+
+#endif /* BuilderUtils_h */
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <set>
+#include <string>
+#include <sstream>
+#include <iomanip> // std::setfill, std::setw
+#include <pthread.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+
+#include <Bom/Bom.h>
+#include <Security/Security.h>
+#include <Security/SecCodeSigner.h>
+#include <CommonCrypto/CommonCrypto.h>
+
+#include "Manifest.h"
+#include "Diagnostics.h"
+#include "FileUtils.h"
+
+#include "BuilderUtils.h"
+
+static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT);
+static dispatch_group_t build_group = dispatch_group_create();
+
+dispatch_group_t buildGroup() {
+ return build_group;
+}
+
+void insertFileInBom(const std::string& path, BOMBom bom)
+{
+ std::vector<std::string> components;
+ std::vector<std::string> processed_components;
+ std::stringstream ss(path);
+ std::string item;
+
+ while (std::getline(ss, item, '/')) {
+ if (!item.empty()) {
+ components.push_back(item);
+ }
+ }
+
+ std::string partialPath = ".";
+ std::string lastComponent = components.back();
+ components.pop_back();
+ BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType);
+ BOMFSObjectSetFlags(fso, B_PATHONLY);
+ BOMFSObjectSetPathName(fso, ".", true);
+ BOMFSObjectSetShortName(fso, ".", true);
+ (void)BOMBomInsertFSObject(bom, fso, false);
+ BOMFSObjectFree(fso);
+
+ for (const auto& component : components) {
+ partialPath = partialPath + "/" + component;
+ fso = BOMFSObjectNew(BOMDirectoryType);
+ BOMFSObjectSetFlags(fso, B_PATHONLY);
+ BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
+ BOMFSObjectSetShortName(fso, component.c_str(), true);
+ (void)BOMBomInsertFSObject(bom, fso, false);
+ BOMFSObjectFree(fso);
+ }
+
+ partialPath = partialPath + "/" + lastComponent;
+ fso = BOMFSObjectNew(BOMFileType);
+ BOMFSObjectSetFlags(fso, B_PATHONLY);
+ BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
+ BOMFSObjectSetShortName(fso, lastComponent.c_str(), true);
+ (void)BOMBomInsertFSObject(bom, fso, false);
+ BOMFSObjectFree(fso);
+}
+
+void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot)
+{
+ mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
+
+ manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) {
+ auto config = manifest.configuration(configName);
+ std::vector<std::string> prodBomPaths;
+ std::vector<std::string> devBomPaths;
+
+ std::string runtimePath = "/System/Library/Caches/com.apple.dyld/";
+ if (manifest.platform() == dyld3::Platform::macOS) {
+ runtimePath = "/private/var/db/dyld/";
+ }
+
+ for (auto& arch : config.architectures) {
+ std::string cachePath = "dyld_shared_cache_" + arch.first;
+ prodBomPaths.push_back(cachePath);
+ if (manifest.platform() != dyld3::Platform::macOS) {
+ cachePath += ".development";
+ }
+ devBomPaths.push_back(cachePath);
+ char buffer[MAXPATHLEN];
+ sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str());
+ BOMBom bom = BOMBomNew(buffer);
+ for (auto& path : prodBomPaths) {
+ insertFileInBom(runtimePath + path, bom);
+ }
+ BOMBomFree(bom);
+
+ sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str());
+ bom = BOMBomNew(buffer);
+ for (auto& path : devBomPaths) {
+ insertFileInBom(runtimePath + path, bom);
+ }
+ BOMBomFree(bom);
+
+ sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str());
+ bom = BOMBomNew(buffer);
+ for (auto& path : prodBomPaths) {
+ insertFileInBom(runtimePath + path, bom);
+ }
+ for (auto& path : devBomPaths) {
+ insertFileInBom(runtimePath + path, bom);
+ }
+ BOMBomFree(bom);
+ }
+ });
+}
+
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose,
+ bool skipWrites, bool agileChooseSHA256CdHash)
+{
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_queue_t warningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", DISPATCH_QUEUE_SERIAL);
+ std::vector<std::set<std::string>> dedupedCacheSets;
+ if (dedupe) {
+ manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
+ auto config = manifest.configuration(configName);
+ bool dupeFound = false;
+
+ for (auto& cacheSet : dedupedCacheSets) {
+ if (config == manifest.configuration(*cacheSet.begin())) {
+ cacheSet.insert(configName);
+ dupeFound = true;
+ break;
+ }
+ }
+
+ if (!dupeFound) {
+ std::set<std::string> temp;
+ temp.insert(configName);
+ dedupedCacheSets.push_back(temp);
+ }
+ });
+ } else {
+ manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
+ std::set<std::string> temp;
+ temp.insert(configName);
+ dedupedCacheSets.push_back(temp);
+ });
+ }
+
+ std::vector<dyld3::BuildQueueEntry> buildQueue;
+
+ 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());
+
+ if (dedupe) {
+ for (auto& config : cacheSet) {
+ if (!skipWrites) {
+ int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str());
+ if (err) {
+ diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err);
+ }
+ }
+ }
+ }
+
+ manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, &buildQueue, &cacheSet, verbose](const std::string& arch) {
+ std::string configPath;
+ std::string runtimePath = "/System/Library/Caches/com.apple.dyld/";
+ if (manifest.platform() == dyld3::Platform::macOS) {
+ runtimePath = "/private/var/db/dyld/";
+ }
+ if (dedupe) {
+ configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath;
+ } else {
+ configPath = masterDstRoot + runtimePath;
+ }
+
+ if (manifest.platform() == dyld3::Platform::macOS) {
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch, verbose));
+ } else {
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch, verbose));
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch, verbose));
+ }
+ });
+ }
+
+ __block bool cacheBuildFailure = false;
+ __block std::set<std::string> warnings;
+ __block std::set<std::string> errors;
+
+ dispatch_sync(warningQueue, ^{
+ auto manifestWarnings = diags.warnings();
+ warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
+ });
+
+ dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
+ auto queueEntry = buildQueue[index];
+ pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str());
+
+ DyldSharedCache::CreateResults results;
+ while (1) {
+ results = DyldSharedCache::create(queueEntry.options, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables);
+ if (!results.overflowed)
+ break;
+ auto evicted = manifest.removeLargestLeafDylib(queueEntry.configNames, queueEntry.options.archName);
+ if (evicted.empty())
+ break;
+ queueEntry = manifest.makeQueueEntry(queueEntry.outputPath, queueEntry.configNames, queueEntry.options.archName, queueEntry.options.optimizeStubs, queueEntry.options.loggingPrefix, queueEntry.options.verbose);
+ dispatch_sync(warningQueue, ^{
+ warnings.insert("[WARNING] CACHE OVERFLOW: " + queueEntry.options.loggingPrefix + " evicted dylib: " + evicted);
+ });
+ }
+ dispatch_sync(warningQueue, ^{
+ warnings.insert(results.warnings.begin(), results.warnings.end());
+ bool chooseSecondCdHash = agileChooseSHA256CdHash;
+ if (agileChooseSHA256CdHash && !results.agileSignature) {
+ // Ignore this option for caches that are not signed agile (which is the majority).
+ chooseSecondCdHash = false;
+ }
+ for (const auto& configName : queueEntry.configNames) {
+ manifest.configuration(configName).architecture(queueEntry.options.archName).results.warnings = results.warnings;
+ if (queueEntry.options.optimizeStubs) {
+ manifest.configuration(configName).architecture(queueEntry.options.archName)
+ .results.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+ } else {
+ manifest.configuration(configName).architecture(queueEntry.options.archName)
+ .results.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+ }
+ }
+ });
+ if (!results.errorMessage.empty()) {
+ fprintf(stderr, "[%s] ERROR: %s\n", queueEntry.options.loggingPrefix.c_str(), results.errorMessage.c_str());
+ } else if (!skipWrites) {
+ dispatch_sync(write_queue, ^{
+ // save new cache file to disk and write new .map file
+ assert(results.cacheContent != nullptr);
+ mkpath_np(dirPath(queueEntry.outputPath).c_str(), 0755);
+ if (!safeSave(results.cacheContent, results.cacheLength, queueEntry.outputPath)) {
+ cacheBuildFailure = true;
+ fprintf(stderr, "[%s] ERROR: Could not write cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+ } else {
+ fprintf(stderr, "[%s] Wrote cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+ std::string mapStr = results.cacheContent->mapFile();
+ std::string outFileMap = queueEntry.outputPath + ".map";
+ safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+ }
+ // free created cache buffer
+ vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+ });
+ } else {
+ fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+ vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+ }
+ });
+
+ // print any warnings
+ for (const std::string& warn : warnings) {
+ fprintf(stderr, "[WARNING] %s\n", warn.c_str());
+ }
+
+ int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
+ if (err) {
+ fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
+ }
+
+ return !cacheBuildFailure;
+}
--- /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 <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach/shared_region.h>
+#include <assert.h>
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+#include <pthread/pthread.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "MachOParser.h"
+#include "CodeSigningTypes.h"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+#include "FileAbstraction.hpp"
+#include "LaunchCacheWriter.h"
+#include "Trie.hpp"
+#include "Diagnostics.h"
+#include "ImageProxy.h"
+
+#if __has_include("dyld_cache_config.h")
+ #include "dyld_cache_config.h"
+#else
+ #define ARM_SHARED_REGION_START 0x1A000000ULL
+ #define ARM_SHARED_REGION_SIZE 0x26000000ULL
+ #define ARM64_SHARED_REGION_START 0x180000000ULL
+ #define ARM64_SHARED_REGION_SIZE 0x40000000ULL
+#endif
+
+const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = {
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64", 0, 0, 0, 12, true, true },
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64h", 0, 0, 0, 12, true, true },
+ { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x00200000, 0x0, "i386", 0, 0, 0, 12, false, false },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64", 0x0000C000, 0x00100000, 0x07F00000, 14, false, true },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64e", 0x0000C000, 0x00100000, 0x07F00000, 14, false, true },
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x02000000, 0xE0000000, "armv7s", 0, 0, 0, 14, false, false },
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x00400000, 0xE0000000, "armv7k", 0, 0, 0, 14, false, false },
+ { 0x40000000, 0x40000000, 0x02000000, 0x0, "sim-x86", 0, 0, 0, 14, false, false }
+};
+
+
+// These are dylibs that may be interposed, so stubs calling into them should never be bypassed
+const char* const CacheBuilder::_s_neverStubEliminate[] = {
+ "/usr/lib/system/libdispatch.dylib",
+ nullptr
+};
+
+
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options)
+ : _options(options)
+ , _buffer(nullptr)
+ , _diagnostics(options.loggingPrefix, options.verbose)
+ , _archLayout(nullptr)
+ , _aliasCount(0)
+ , _slideInfoFileOffset(0)
+ , _slideInfoBufferSizeAllocated(0)
+ , _allocatedBufferSize(0)
+ , _currentFileSize(0)
+ , _vmSize(0)
+ , _branchPoolsLinkEditStartAddr(0)
+{
+
+ std::string targetArch = options.archName;
+ if ( options.forSimulator && (options.archName == "i386") )
+ targetArch = "sim-x86";
+
+ for (const ArchLayout& layout : _s_archLayout) {
+ if ( layout.archName == targetArch ) {
+ _archLayout = &layout;
+ break;
+ }
+ }
+}
+
+
+std::string CacheBuilder::errorMessage()
+{
+ return _diagnostics.errorMessage();
+}
+
+const std::set<std::string> CacheBuilder::warnings()
+{
+ return _diagnostics.warnings();
+}
+
+void CacheBuilder::deleteBuffer()
+{
+ vm_deallocate(mach_task_self(), (vm_address_t)_buffer, _allocatedBufferSize);
+ _buffer = nullptr;
+ _allocatedBufferSize = 0;
+}
+
+std::vector<DyldSharedCache::MappedMachO>
+CacheBuilder::makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+{
+ std::vector<DyldSharedCache::MappedMachO> sortedDylibs = dylibs;
+
+ std::sort(sortedDylibs.begin(), sortedDylibs.end(), [&](const DyldSharedCache::MappedMachO& a, const DyldSharedCache::MappedMachO& b) {
+ const auto& orderA = sortOrder.find(a.runtimePath);
+ const auto& orderB = sortOrder.find(b.runtimePath);
+ bool foundA = (orderA != sortOrder.end());
+ bool foundB = (orderB != sortOrder.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 a.runtimePath < b.runtimePath;
+ });
+
+ return sortedDylibs;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+ return (uint32_t)(abstime/1000/1000);
+}
+
+struct DylibAndSize
+{
+ const char* installName;
+ uint64_t size;
+};
+
+bool CacheBuilder::cacheOverflow(const dyld_cache_mapping_info regions[3])
+{
+ if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+ // for macOS x86_64 cache, need to check each region for overflow
+ return ( (regions[0].size > 0x60000000) || (regions[1].size > 0x40000000) || (regions[2].size > 0x3FE00000) );
+ }
+ else {
+ return (_vmSize > _archLayout->sharedMemorySize);
+ }
+}
+
+bool CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables)
+{
+ // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
+ // FIXME: plist should specify required vs optional dylibs
+ if ( dylibs.size() < 30 ) {
+ _diagnostics.error("missing required minimum set of dylibs");
+ return false;
+ }
+ uint64_t t1 = mach_absolute_time();
+
+
+ // make copy of dylib list and sort
+ std::vector<DyldSharedCache::MappedMachO> sortedDylibs = makeSortedDylibs(dylibs, _options.dylibOrdering);
+ std::vector<DyldSharedCache::MappedMachO> otherOsDylibs = otherOsDylibsInput;
+
+ // assign addresses for each segment of each dylib in new cache
+ dyld_cache_mapping_info regions[3];
+ SegmentMapping segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
+ if ( cacheOverflow(regions) ) {
+ if ( !_options.evictLeafDylibsOnOverflow ) {
+ _diagnostics.error("cache overflow: %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+ return false;
+ }
+ // find all leaf (not referenced by anything else in cache) dylibs
+
+ // build count of how many references there are to each dylib
+ __block std::map<std::string, unsigned int> referenceCount;
+ for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) {
+ dyld3::MachOParser parser(dylib.mh);
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ referenceCount[loadPath] += 1;
+ });
+ }
+
+ // find all dylibs not referenced
+ std::vector<DylibAndSize> unreferencedDylibs;
+ for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) {
+ dyld3::MachOParser parser(dylib.mh);
+ const char* installName = parser.installName();
+ if ( referenceCount.count(installName) == 0 ) {
+ // conservative: sum up all segments except LINKEDIT
+ __block uint64_t segsSize = 0;
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) {
+ if ( strcmp(segName, "__LINKEDIT") != 0 )
+ segsSize += vmSize;
+ });
+ unreferencedDylibs.push_back({installName, segsSize});
+ }
+ }
+ // sort leaf dylibs by size
+ std::sort(unreferencedDylibs.begin(), unreferencedDylibs.end(), [&](const DylibAndSize& a, const DylibAndSize& b) {
+ return ( a.size > b.size );
+ });
+
+ // build set of dylibs that if removed will allow cache to build
+ uint64_t reductionTarget = _vmSize - _archLayout->sharedMemorySize;
+ std::set<std::string> toRemove;
+ for (DylibAndSize& dylib : unreferencedDylibs) {
+ if ( _options.verbose )
+ _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+ toRemove.insert(dylib.installName);
+ if ( dylib.size > reductionTarget )
+ break;
+ reductionTarget -= dylib.size;
+ }
+ // transfer overflow dylibs from cached vector to other vector
+ for (const std::string& installName : toRemove) {
+ for (std::vector<DyldSharedCache::MappedMachO>::iterator it=sortedDylibs.begin(); it != sortedDylibs.end(); ++it) {
+ dyld3::MachOParser parser(it->mh);
+ if ( installName == parser.installName() ) {
+ otherOsDylibs.push_back(*it);
+ sortedDylibs.erase(it);
+ break;
+ }
+ }
+ }
+ // re-layout cache
+ segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
+ if ( cacheOverflow(regions) ) {
+ _diagnostics.error("cache overflow, tried evicting %ld leaf daylibs, but still too big: %lluMB (max %lluMB)",
+ toRemove.size(), _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+ return false;
+ }
+ }
+
+ // allocate buffer for new cache
+ _allocatedBufferSize = std::max(_currentFileSize, (uint64_t)0x100000)*1.1; // add 10% to allocation to support large closures
+ if ( vm_allocate(mach_task_self(), (vm_address_t*)&_buffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
+ _diagnostics.error("could not allocate buffer");
+ return false;
+ }
+ _currentFileSize = _allocatedBufferSize;
+
+ // write unoptimized cache
+ writeCacheHeader(regions, sortedDylibs, segmentMapping);
+ copyRawSegments(sortedDylibs, segmentMapping);
+ adjustAllImagesForNewSegmentLocations(sortedDylibs, segmentMapping);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ bindAllImagesInCacheFile(regions);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ // optimize ObjC
+ if ( _options.optimizeObjC )
+ optimizeObjC(_buffer, _archLayout->is64, _options.optimizeStubs, _pointersForASLR, _diagnostics);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ // optimize away stubs
+ std::vector<uint64_t> branchPoolOffsets;
+ uint64_t cacheStartAddress = _archLayout->sharedMemoryStart;
+ if ( _options.optimizeStubs ) {
+ std::vector<uint64_t> branchPoolStartAddrs;
+ const uint64_t* p = (uint64_t*)((uint8_t*)_buffer + _buffer->header.branchPoolsOffset);
+ for (int i=0; i < _buffer->header.branchPoolsCount; ++i) {
+ uint64_t poolAddr = p[i];
+ branchPoolStartAddrs.push_back(poolAddr);
+ branchPoolOffsets.push_back(poolAddr - cacheStartAddress);
+ }
+ bypassStubs(_buffer, branchPoolStartAddrs, _s_neverStubEliminate, _diagnostics);
+ }
+ uint64_t t2 = mach_absolute_time();
+
+ // FIPS seal corecrypto, This must be done after stub elimination (so that
+ // __TEXT,__text is not changed after sealing), but before LINKEDIT
+ // optimization (so that we still have access to local symbols)
+ fipsSign();
+
+ // merge and compact LINKEDIT segments
+ dyld_cache_local_symbols_info* localsInfo = nullptr;
+ if ( dylibs.size() == 0 )
+ _currentFileSize = 0x1000;
+ else
+ _currentFileSize = optimizeLinkedit(_buffer, _archLayout->is64, _options.excludeLocalSymbols, _options.optimizeStubs, branchPoolOffsets, _diagnostics, &localsInfo);
+
+ uint64_t t3 = mach_absolute_time();
+
+ // add ImageGroup for all dylibs in cache
+ __block std::vector<DyldSharedCache::MappedMachO> cachedDylibs;
+ std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> mapIntoSortedDylibs;
+ for (const DyldSharedCache::MappedMachO& entry : sortedDylibs) {
+ mapIntoSortedDylibs[entry.runtimePath] = &entry;
+ }
+ _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+ auto pos = mapIntoSortedDylibs.find(installName);
+ if ( pos != mapIntoSortedDylibs.end() ) {
+ DyldSharedCache::MappedMachO newEntry = *(pos->second);
+ newEntry.mh = mh;
+ cachedDylibs.push_back(newEntry);
+ }
+ else {
+ bool found = false;
+ for (const std::string& prefix : _options.pathPrefixes) {
+ std::string fullPath = prefix + installName;
+ char resolvedPath[PATH_MAX];
+ if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) {
+ std::string resolvedUnPrefixed = &resolvedPath[prefix.size()];
+ pos = mapIntoSortedDylibs.find(resolvedUnPrefixed);
+ if ( pos != mapIntoSortedDylibs.end() ) {
+ DyldSharedCache::MappedMachO newEntry = *(pos->second);
+ newEntry.mh = mh;
+ cachedDylibs.push_back(newEntry);
+ found = true;
+ }
+ }
+ }
+ if ( !found )
+ fprintf(stderr, "missing mapping for %s\n", installName);
+ }
+ });
+ dyld3::DyldCacheParser dyldCacheParser(_buffer, true);
+ dyld3::ImageProxyGroup* dylibGroup = dyld3::ImageProxyGroup::makeDyldCacheDylibsGroup(_diagnostics, dyldCacheParser, cachedDylibs,
+ _options.pathPrefixes, _patchTable,
+ _options.optimizeStubs, !_options.dylibsRemovedDuringMastering);
+ if ( _diagnostics.hasError() )
+ return false;
+ addCachedDylibsImageGroup(dylibGroup);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ uint64_t t4 = mach_absolute_time();
+
+ // add ImageGroup for other OS dylibs and bundles
+ dyld3::ImageProxyGroup* otherGroup = dyld3::ImageProxyGroup::makeOtherOsGroup(_diagnostics, dyldCacheParser, dylibGroup, otherOsDylibs,
+ _options.inodesAreSameAsRuntime, _options.pathPrefixes);
+ if ( _diagnostics.hasError() )
+ return false;
+ addCachedOtherDylibsImageGroup(otherGroup);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ uint64_t t5 = mach_absolute_time();
+
+ // compute and add launch closures
+ std::map<std::string, const dyld3::launch_cache::binary_format::Closure*> closures;
+ for (const DyldSharedCache::MappedMachO& mainProg : osExecutables) {
+ Diagnostics clsDiag;
+ const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(clsDiag, dyldCacheParser, dylibGroup, otherGroup, mainProg,
+ _options.inodesAreSameAsRuntime, _options.pathPrefixes);
+ if ( clsDiag.hasError() ) {
+ // if closure cannot be built, silently skip it, unless in verbose mode
+ if ( _options.verbose ) {
+ _diagnostics.warning("building closure for '%s': %s", mainProg.runtimePath.c_str(), clsDiag.errorMessage().c_str());
+ for (const std::string& warn : clsDiag.warnings() )
+ _diagnostics.warning("%s", warn.c_str());
+ }
+ }
+ else {
+ closures[mainProg.runtimePath] = cls;
+ }
+ }
+ addClosures(closures);
+ if ( _diagnostics.hasError() )
+ return false;
+
+ uint64_t t6 = mach_absolute_time();
+
+ // fill in slide info at start of region[2]
+ // do this last because it modifies pointers in DATA segments
+ if ( _options.cacheSupportsASLR ) {
+ if ( _archLayout->is64 )
+ writeSlideInfoV2<Pointer64<LittleEndian>>();
+ else
+ writeSlideInfoV2<Pointer32<LittleEndian>>();
+ }
+
+ uint64_t t7 = mach_absolute_time();
+
+ // update last region size
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ _currentFileSize = align(_currentFileSize, _archLayout->sharedRegionAlignP2);
+ mappings[2].size = _currentFileSize - mappings[2].fileOffset;
+
+ // record cache bounds
+ _buffer->header.sharedRegionStart = _archLayout->sharedMemoryStart;
+ _buffer->header.sharedRegionSize = _archLayout->sharedMemorySize;
+ if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+ // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions
+ uint64_t maxSlide0 = 0x60000000 - mappings[0].size; // TEXT region has 1.5GB region
+ uint64_t maxSlide1 = 0x40000000 - mappings[1].size;
+ uint64_t maxSlide2 = 0x3FE00000 - mappings[2].size;
+ _buffer->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
+ }
+ else {
+ _buffer->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (mappings[2].address + mappings[2].size);
+ }
+
+ // append "unmapped" local symbols region
+ if ( _options.excludeLocalSymbols ) {
+ size_t localsInfoSize = align(localsInfo->stringsOffset + localsInfo->stringsSize, _archLayout->sharedRegionAlignP2);
+ if ( _currentFileSize + localsInfoSize > _allocatedBufferSize ) {
+ _diagnostics.warning("local symbols omitted because cache buffer overflow");
+ }
+ else {
+ memcpy((char*)_buffer+_currentFileSize, localsInfo, localsInfoSize);
+ _buffer->header.localSymbolsOffset = _currentFileSize;
+ _buffer->header.localSymbolsSize = localsInfoSize;
+ _currentFileSize += localsInfoSize;
+ }
+ free((void*)localsInfo);
+ }
+
+ recomputeCacheUUID();
+
+ // Calculate the VMSize of the resulting cache
+ __block uint64_t endAddr = 0;
+ _buffer->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if (vmAddr+size > endAddr)
+ endAddr = vmAddr+size;
+ });
+ _vmSize = endAddr - cacheStartAddress;
+
+ // last sanity check on size
+ if ( _vmSize > _archLayout->sharedMemorySize ) {
+ _diagnostics.error("cache overflow after optimizations. %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+ return true;
+ }
+
+ // codesignature is part of file, but is not mapped
+ codeSign();
+ if ( _diagnostics.hasError() )
+ return false;
+
+ uint64_t t8 = mach_absolute_time();
+
+ if ( _options.verbose ) {
+ fprintf(stderr, "time to copy and bind cached dylibs: %ums\n", absolutetime_to_milliseconds(t2-t1));
+ fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t3-t2));
+ fprintf(stderr, "time to build ImageGroup of %lu cached dylibs: %ums\n", sortedDylibs.size(), absolutetime_to_milliseconds(t4-t3));
+ fprintf(stderr, "time to build ImageGroup of %lu other dylibs: %ums\n", otherOsDylibs.size(), absolutetime_to_milliseconds(t5-t4));
+ fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t6-t5));
+ fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t7-t6));
+ fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t8-t7));
+ }
+
+ // trim over allocated buffer
+ if ( _allocatedBufferSize > _currentFileSize ) {
+ uint8_t* startOfUnused = (uint8_t*)_buffer+_currentFileSize;
+ size_t unusedLen = _allocatedBufferSize-_currentFileSize;
+ vm_deallocate(mach_task_self(), (vm_address_t)startOfUnused, unusedLen);
+ _allocatedBufferSize = _currentFileSize;
+ }
+
+ return false;
+}
+
+
+void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& segmentMappings)
+{
+ // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
+ std::string magic = "dyld_v1";
+ magic.append(15 - magic.length() - _options.archName.length(), ' ');
+ magic.append(_options.archName);
+ assert(magic.length() == 15);
+
+ // fill in header
+ memcpy(_buffer->header.magic, magic.c_str(), 16);
+ _buffer->header.mappingOffset = sizeof(dyld_cache_header);
+ _buffer->header.mappingCount = 3;
+ _buffer->header.imagesOffset = (uint32_t)(_buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info) + sizeof(uint64_t)*_branchPoolStarts.size());
+ _buffer->header.imagesCount = (uint32_t)dylibs.size() + _aliasCount;
+ _buffer->header.dyldBaseAddress = 0;
+ _buffer->header.codeSignatureOffset= 0;
+ _buffer->header.codeSignatureSize = 0;
+ _buffer->header.slideInfoOffset = _slideInfoFileOffset;
+ _buffer->header.slideInfoSize = _slideInfoBufferSizeAllocated;
+ _buffer->header.localSymbolsOffset = 0;
+ _buffer->header.localSymbolsSize = 0;
+ _buffer->header.cacheType = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+ _buffer->header.accelerateInfoAddr = 0;
+ _buffer->header.accelerateInfoSize = 0;
+ bzero(_buffer->header.uuid, 16); // overwritten later by recomputeCacheUUID()
+ _buffer->header.branchPoolsOffset = _buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info);
+ _buffer->header.branchPoolsCount = (uint32_t)_branchPoolStarts.size();
+ _buffer->header.imagesTextOffset = _buffer->header.imagesOffset + sizeof(dyld_cache_image_info)*_buffer->header.imagesCount;
+ _buffer->header.imagesTextCount = dylibs.size();
+ _buffer->header.platform = (uint8_t)_options.platform;
+ _buffer->header.formatVersion = dyld3::launch_cache::binary_format::kFormatVersion;
+ _buffer->header.dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+ _buffer->header.simulator = _options.forSimulator;
+
+ // fill in mappings
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ mappings[0] = regions[0];
+ mappings[1] = regions[1];
+ mappings[2] = regions[2];
+
+ // fill in branch pool addresses
+ uint64_t* p = (uint64_t*)((char*)_buffer + _buffer->header.branchPoolsOffset);
+ for (uint64_t pool : _branchPoolStarts) {
+ *p++ = pool;
+ }
+
+ // fill in image table
+ dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset);
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ const std::vector<SegmentMappingInfo>& segs = segmentMappings.at(dylib.mh);
+ dyld3::MachOParser parser(dylib.mh);
+ const char* installName = parser.installName();
+ images->address = segs[0].dstCacheAddress;
+ if ( _options.dylibsRemovedDuringMastering ) {
+ images->modTime = 0;
+ images->inode = pathHash(installName);
+ }
+ else {
+ images->modTime = dylib.modTime;
+ images->inode = dylib.inode;
+ }
+ uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.mh);
+ images->pathFileOffset = (uint32_t)segs[0].dstCacheOffset + installNameOffsetInTEXT;
+ ++images;
+ }
+ // append aliases image records and strings
+/*
+ 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->modTime = dylib->lastModTime;
+ images->inode = dylib->inode;
+ }
+ else {
+ images->modTime = 0;
+ images->inode = pathHash(alias.c_str());
+ }
+ images->pathFileOffset = offset;
+ //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
+ ::strcpy((char*)&_buffer[offset], alias.c_str());
+ offset += alias.size() + 1;
+ ++images;
+ }
+ }
+ }
+*/
+ // calculate start of text image array and trailing string pool
+ dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)((char*)_buffer + _buffer->header.imagesTextOffset);
+ uint32_t stringOffset = (uint32_t)(_buffer->header.imagesTextOffset + sizeof(dyld_cache_image_text_info) * dylibs.size());
+
+ // write text image array and image names pool at same time
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ const std::vector<SegmentMappingInfo>& segs = segmentMappings.at(dylib.mh);
+ dyld3::MachOParser parser(dylib.mh);
+ parser.getUuid(textImages->uuid);
+ textImages->loadAddress = segs[0].dstCacheAddress;
+ textImages->textSegmentSize = (uint32_t)segs[0].dstCacheSegmentSize;
+ textImages->pathOffset = stringOffset;
+ const char* installName = parser.installName();
+ ::strcpy((char*)_buffer + stringOffset, installName);
+ stringOffset += (uint32_t)strlen(installName)+1;
+ ++textImages;
+ }
+
+ // make sure header did not overflow into first mapped image
+ const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset);
+ assert(stringOffset <= (firstImage->address - mappings[0].address));
+}
+
+
+void CacheBuilder::copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+{
+ uint8_t* cacheBytes = (uint8_t*)_buffer;
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ auto pos = mapping.find(dylib.mh);
+ assert(pos != mapping.end());
+ for (const SegmentMappingInfo& info : pos->second) {
+ //fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n", _options.archName.c_str(), info.segName, info.copySegmentSize, info.srcSegment, &cacheBytes[info.dstCacheOffset], info.dstCacheAddress, dylib.runtimePath.c_str());
+ ::memcpy(&cacheBytes[info.dstCacheOffset], info.srcSegment, info.copySegmentSize);
+ }
+ }
+}
+
+void CacheBuilder::adjustAllImagesForNewSegmentLocations(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+{
+ uint8_t* cacheBytes = (uint8_t*)_buffer;
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ auto pos = mapping.find(dylib.mh);
+ assert(pos != mapping.end());
+ mach_header* mhInCache = (mach_header*)&cacheBytes[pos->second[0].dstCacheOffset];
+ adjustDylibSegments(_buffer, _archLayout->is64, mhInCache, pos->second, _pointersForASLR, _diagnostics);
+ if ( _diagnostics.hasError() )
+ break;
+ }
+}
+
+struct Counts {
+ unsigned long lazyCount = 0;
+ unsigned long nonLazyCount = 0;
+};
+
+void CacheBuilder::bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3])
+{
+ const bool log = false;
+ __block std::unordered_map<std::string, Counts> useCounts;
+
+ // build map of install names to mach_headers
+ __block std::unordered_map<std::string, const mach_header*> installNameToMH;
+ __block std::vector<const mach_header*> dylibMHs;
+ _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+ installNameToMH[installName] = mh;
+ dylibMHs.push_back(mh);
+ });
+
+ __block Diagnostics parsingDiag;
+ bool (^dylibFinder)(uint32_t, const char*, void* , const mach_header**, void**) = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ auto pos = installNameToMH.find(depLoadPath);
+ if ( pos != installNameToMH.end() ) {
+ *foundMH = pos->second;
+ *foundExtra = nullptr;
+ return true;
+ }
+ parsingDiag.error("dependent dylib %s not found", depLoadPath);
+ return false;
+ };
+ if ( parsingDiag.hasError() ) {
+ _diagnostics.error("%s", parsingDiag.errorMessage().c_str());
+ return;
+ }
+
+ // bind every dylib in cache
+ for (const mach_header* mh : dylibMHs) {
+ dyld3::MachOParser parser(mh, true);
+ bool is64 = parser.is64();
+ const char* depPaths[256];
+ const char** depPathsArray = depPaths;
+ __block int depIndex = 1;
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ depPathsArray[depIndex++] = loadPath;
+ });
+ uint8_t* segCacheStarts[10];
+ uint64_t segCacheAddrs[10];
+ uint8_t** segCacheStartsArray = segCacheStarts;
+ uint64_t* segCacheAddrsArray = segCacheAddrs;
+ __block int segIndex = 0;
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ segCacheStartsArray[segIndex] = (segIndex == 0) ? (uint8_t*)mh : (uint8_t*)_buffer + fileOffset;
+ segCacheAddrsArray[segIndex] = vmAddr;
+ ++segIndex;
+ });
+ __block Diagnostics bindingDiag;
+ parser.forEachBind(bindingDiag, ^(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) {
+ if ( log ) {
+ if ( lazy )
+ useCounts[symbolName].lazyCount += 1;
+ else
+ useCounts[symbolName].nonLazyCount += 1;
+ }
+ const mach_header* targetMH = nullptr;
+ if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+ targetMH = mh;
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+ parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE not supported in dylibs in dyld shared cache (found in %s)", parser.installName());
+ stop = true;
+ return;
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+ parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_FLAT_LOOKUP not supported in dylibs in dyld shared cache (found in %s)", parser.installName());
+ stop = true;
+ return;
+ }
+ else {
+ const char* fromPath = depPathsArray[libOrdinal];
+ auto pos = installNameToMH.find(fromPath);
+ if (pos == installNameToMH.end()) {
+ if (!weakImport) {
+ _diagnostics.error("dependent dylib %s not found", fromPath);
+ }
+ return;
+ }
+ targetMH = pos->second;
+ }
+ dyld3::MachOParser targetParser(targetMH, true);
+ dyld3::MachOParser::FoundSymbol foundInfo;
+ uint64_t targetValue = 0;
+ uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset;
+ if ( targetParser.findExportedSymbol(parsingDiag, symbolName, nullptr, foundInfo, dylibFinder) ) {
+ const mach_header* foundInMH = foundInfo.foundInDylib;
+ dyld3::MachOParser foundInParser(foundInMH, true);
+ uint64_t foundInBaseAddress = foundInParser.preferredLoadAddress();
+ switch ( foundInfo.kind ) {
+ case dyld3::MachOParser::FoundSymbol::Kind::resolverOffset:
+ // Bind to the target stub for resolver based functions.
+ // There may be a later optimization to alter the client
+ // stubs to directly to the target stub's lazy pointer.
+ case dyld3::MachOParser::FoundSymbol::Kind::headerOffset:
+ targetValue = foundInBaseAddress + foundInfo.value + addend;
+ _pointersForASLR.push_back((void*)fixupLoc);
+ if ( foundInMH != mh ) {
+ uint32_t mhVmOffset = (uint32_t)((uint8_t*)foundInMH - (uint8_t*)_buffer);
+ uint32_t definitionCacheVmOffset = (uint32_t)(mhVmOffset + foundInfo.value);
+ uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address);
+ assert(referenceCacheDataVmOffset < (1<<30));
+ dyld3::launch_cache::binary_format::PatchOffset entry;
+ entry.last = false;
+ entry.hasAddend = (addend != 0);
+ entry.dataRegionOffset = referenceCacheDataVmOffset;
+ _patchTable[foundInMH][definitionCacheVmOffset].insert(*((uint32_t*)&entry));
+ }
+ break;
+ case dyld3::MachOParser::FoundSymbol::Kind::absolute:
+ // pointers set to absolute values are not slid
+ targetValue = foundInfo.value + addend;
+ break;
+ }
+ }
+ else if ( weakImport ) {
+ // weak pointers set to zero are not slid
+ targetValue = 0;
+ }
+ else {
+ parsingDiag.error("cannot find symbol %s, needed in dylib %s", symbolName, parser.installName());
+ stop = true;
+ }
+ switch ( type ) {
+ case BIND_TYPE_POINTER:
+ if ( is64 )
+ *((uint64_t*)fixupLoc) = targetValue;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)targetValue;
+ break;
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ case BIND_TYPE_TEXT_PCREL32:
+ parsingDiag.error("text relocs not supported for shared cache binding in %s", parser.installName());
+ stop = true;
+ break;
+ default:
+ parsingDiag.error("bad bind type (%d) in %s", type, parser.installName());
+ stop = true;
+ break;
+
+ }
+ });
+ if ( bindingDiag.hasError() ) {
+ parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName());
+ }
+ if ( parsingDiag.hasError() )
+ break;
+ // also need to add patch locations for weak-binds that point within same image, since they are not captured by binds above
+ parser.forEachWeakDef(bindingDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool &stop) {
+ if ( strongDef )
+ return;
+ uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset;
+ dyld3::MachOParser::FoundSymbol weakFoundInfo;
+ Diagnostics weakLookupDiag;
+ if ( parser.findExportedSymbol(weakLookupDiag, symbolName, nullptr, weakFoundInfo, nullptr) ) {
+ // this is an interior pointing (rebased) pointer
+ uint64_t targetValue;
+ if ( is64 )
+ targetValue = *((uint64_t*)fixupLoc);
+ else
+ targetValue = *((uint32_t*)fixupLoc);
+ uint32_t definitionCacheVmOffset = (uint32_t)(targetValue - regions[0].address);
+ uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address);
+ assert(referenceCacheDataVmOffset < (1<<30));
+ dyld3::launch_cache::binary_format::PatchOffset entry;
+ entry.last = false;
+ entry.hasAddend = (addend != 0);
+ entry.dataRegionOffset = referenceCacheDataVmOffset;
+ _patchTable[mh][definitionCacheVmOffset].insert(*((uint32_t*)&entry));
+ }
+ });
+ if ( bindingDiag.hasError() ) {
+ parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName());
+ }
+ if ( parsingDiag.hasError() )
+ break;
+ }
+
+ if ( log ) {
+ unsigned lazyCount = 0;
+ unsigned nonLazyCount = 0;
+ std::unordered_set<std::string> lazyTargets;
+ for (auto entry : useCounts) {
+ fprintf(stderr, "% 3ld % 3ld %s\n", entry.second.lazyCount, entry.second.nonLazyCount, entry.first.c_str());
+ lazyCount += entry.second.lazyCount;
+ nonLazyCount += entry.second.nonLazyCount;
+ if ( entry.second.lazyCount != 0 )
+ lazyTargets.insert(entry.first);
+ }
+ fprintf(stderr, "lazyCount = %d\n", lazyCount);
+ fprintf(stderr, "nonLazyCount = %d\n", nonLazyCount);
+ fprintf(stderr, "unique lazys = %ld\n", lazyTargets.size());
+ }
+
+ if ( parsingDiag.hasError() )
+ _diagnostics.error("%s", parsingDiag.errorMessage().c_str());
+}
+
+
+void CacheBuilder::recomputeCacheUUID(void)
+{
+ // Clear existing UUID, then MD5 whole cache buffer.
+ uint8_t* uuidLoc = _buffer->header.uuid;
+ bzero(uuidLoc, 16);
+ CC_MD5(_buffer, (unsigned)_currentFileSize, 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;
+}
+
+
+CacheBuilder::SegmentMapping CacheBuilder::assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, dyld_cache_mapping_info regions[3])
+{
+ // calculate size of header info and where first dylib's mach_header should start
+ size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info);
+ size_t maxPoolCount = 0;
+ if ( _archLayout->branchReach != 0 )
+ maxPoolCount = (_archLayout->sharedMemorySize / _archLayout->branchReach);
+ startOffset += maxPoolCount * sizeof(uint64_t);
+ startOffset += sizeof(dyld_cache_image_info) * dylibs.size();
+ startOffset += sizeof(dyld_cache_image_text_info) * dylibs.size();
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh);
+ startOffset += (strlen(parser.installName()) + 1);
+ }
+ //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
+ startOffset = align(startOffset, 12);
+
+ _branchPoolStarts.clear();
+ __block uint64_t addr = _archLayout->sharedMemoryStart;
+ __block SegmentMapping result;
+
+ // assign TEXT segment addresses
+ regions[0].address = addr;
+ regions[0].fileOffset = 0;
+ regions[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ regions[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ addr += startOffset; // header
+
+ __block uint64_t lastPoolAddress = addr;
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
+ return;
+ // Insert branch island pools every 128MB for arm64
+ if ( (_archLayout->branchPoolTextSize != 0) && ((addr + vmSize - lastPoolAddress) > _archLayout->branchReach) ) {
+ _branchPoolStarts.push_back(addr);
+ _diagnostics.verbose("adding branch pool at 0x%llX\n", addr);
+ lastPoolAddress = addr;
+ addr += _archLayout->branchPoolTextSize;
+ }
+ // Keep __TEXT segments 4K or more aligned
+ addr = align(addr, std::max(p2align, (uint8_t)12));
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[0].address + regions[0].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+ info.copySegmentSize = (uint32_t)align(sizeOfSections, 12);
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+ // align TEXT region end
+ uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
+ regions[0].size = endTextAddress - regions[0].address;
+
+ // assign __DATA* addresses
+ if ( _archLayout->sharedRegionsAreDiscontiguous )
+ addr = _archLayout->sharedMemoryStart + 0x60000000;
+ else
+ addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
+ regions[1].address = addr;
+ regions[1].fileOffset = regions[0].fileOffset + regions[0].size;
+ regions[1].initProt = VM_PROT_READ | VM_PROT_WRITE;
+ regions[1].maxProt = VM_PROT_READ | VM_PROT_WRITE;
+
+ // layout all __DATA_CONST segments
+ __block int dataConstSegmentCount = 0;
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+ return;
+ if ( strcmp(segName, "__DATA_CONST") != 0 )
+ return;
+ ++dataConstSegmentCount;
+ // Pack __DATA_CONST segments
+ addr = align(addr, p2align);
+ size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+ info.copySegmentSize = (uint32_t)copySize;
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+
+ // layout all __DATA segments (and other r/w non-dirty, non-const) segments
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+ return;
+ if ( strcmp(segName, "__DATA_CONST") == 0 )
+ return;
+ if ( strcmp(segName, "__DATA_DIRTY") == 0 )
+ return;
+ if ( dataConstSegmentCount > 10 ) {
+ // Pack __DATA segments only if we also have __DATA_CONST segments
+ addr = align(addr, p2align);
+ }
+ else {
+ // Keep __DATA segments 4K or more aligned
+ addr = align(addr, std::max(p2align, (uint8_t)12));
+ }
+ size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+ info.copySegmentSize = (uint32_t)copySize;
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+
+ // layout all __DATA_DIRTY segments, sorted
+ addr = align(addr, 12);
+ std::vector<DyldSharedCache::MappedMachO> dirtyDataDylibs = makeSortedDylibs(dylibs, _options.dirtyDataSegmentOrdering);
+ for (const DyldSharedCache::MappedMachO& dylib : dirtyDataDylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+ return;
+ if ( strcmp(segName, "__DATA_DIRTY") != 0 )
+ return;
+ // Pack __DATA_DIRTY segments
+ addr = align(addr, p2align);
+ size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+ info.copySegmentSize = (uint32_t)copySize;
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+
+ // align DATA region end
+ uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
+ regions[1].size = endDataAddress - regions[1].address;
+
+ // start read-only region
+ if ( _archLayout->sharedRegionsAreDiscontiguous )
+ addr = _archLayout->sharedMemoryStart + 0xA0000000;
+ else
+ addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
+ regions[2].address = addr;
+ regions[2].fileOffset = regions[1].fileOffset + regions[1].size;
+ regions[2].maxProt = VM_PROT_READ;
+ regions[2].initProt = VM_PROT_READ;
+
+ // reserve space for kernel ASLR slide info at start of r/o region
+ if ( _options.cacheSupportsASLR ) {
+ _slideInfoBufferSizeAllocated = align((regions[1].size/4096) * 4, _archLayout->sharedRegionAlignP2); // only need 2 bytes per page
+ _slideInfoFileOffset = regions[2].fileOffset;
+ addr += _slideInfoBufferSizeAllocated;
+ }
+
+ // layout all read-only (but not LINKEDIT) segments
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != VM_PROT_READ )
+ return;
+ if ( strcmp(segName, "__LINKEDIT") == 0 )
+ return;
+ // Keep segments segments 4K or more aligned
+ addr = align(addr, std::max(p2align, (uint8_t)12));
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[2].address + regions[2].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+ info.copySegmentSize = (uint32_t)sizeOfSections;
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+ // layout all LINKEDIT segments (after other read-only segments)
+ for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+ dyld3::MachOParser parser(dylib.mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+ if ( protections != VM_PROT_READ )
+ return;
+ if ( strcmp(segName, "__LINKEDIT") != 0 )
+ return;
+ // Keep segments segments 4K or more aligned
+ addr = align(addr, std::max(p2align, (uint8_t)12));
+ SegmentMappingInfo info;
+ info.srcSegment = (uint8_t*)dylib.mh + fileOffset;
+ info.segName = segName;
+ info.dstCacheAddress = addr;
+ info.dstCacheOffset = (uint32_t)(addr - regions[2].address + regions[2].fileOffset);
+ info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+ info.copySegmentSize = (uint32_t)align(fileSize, 12);
+ info.srcSegmentIndex = segIndex;
+ result[dylib.mh].push_back(info);
+ addr += info.dstCacheSegmentSize;
+ });
+ }
+ // add room for branch pool linkedits
+ _branchPoolsLinkEditStartAddr = addr;
+ addr += (_branchPoolStarts.size() * _archLayout->branchPoolLinkEditSize);
+
+ // align r/o region end
+ uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+ regions[2].size = endReadOnlyAddress - regions[2].address;
+ _currentFileSize = regions[2].fileOffset + regions[2].size;
+
+ // FIXME: Confirm these numbers for all platform/arch combos
+ // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size
+ if ( _options.excludeLocalSymbols ) {
+ _vmSize = regions[2].address + (regions[2].size * 2 / 5) - regions[0].address;
+ }
+ else {
+ _vmSize = regions[2].address + (regions[2].size * 9 / 10) - regions[0].address;
+ }
+
+ // sort SegmentMappingInfo for each image to be in the same order as original segments
+ for (auto& entry : result) {
+ std::vector<SegmentMappingInfo>& infos = entry.second;
+ std::sort(infos.begin(), infos.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+ return a.srcSegmentIndex < b.srcSegmentIndex;
+ });
+ }
+
+ return result;
+}
+
+uint64_t CacheBuilder::pathHash(const char* path)
+{
+ uint64_t sum = 0;
+ for (const char* s=path; *s != '\0'; ++s)
+ sum += sum*4 + *s;
+ return sum;
+}
+
+
+void CacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName)
+{
+ foundDylibName = "???";
+ foundSegName = "???";
+ uint32_t cacheOffset = (uint32_t)((uint8_t*)contentPtr - (uint8_t*)_buffer);
+ _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+ dyld3::MachOParser parser(mh, true);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( (cacheOffset > fileOffset) && (cacheOffset < (fileOffset+vmSize)) ) {
+ foundDylibName = installName;
+ foundSegName = segName;
+ }
+ });
+ });
+ }
+
+
+template <typename P>
+bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* 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);
+ _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+ lastLocationOffset, segName.c_str(), dylibName.c_str());
+ return false;
+ }
+ 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 CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* 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 ) {
+ _diagnostics.error("rebase overflow in page extras");
+ return;
+ }
+ 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 CacheBuilder::writeSlideInfoV2()
+{
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+ const uint32_t pageSize = 4096;
+
+ // build one 1024/4096 bool bitmap per page (4KB/16KB) of DATA
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ uint8_t* const dataStart = (uint8_t*)_buffer + mappings[1].fileOffset;
+ uint8_t* const dataEnd = dataStart + mappings[1].size;
+ unsigned pageCount = (unsigned)(mappings[1].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) ) {
+ _diagnostics.error("DATA pointer for sliding, out of range\n");
+ free(bitmap);
+ return;
+ }
+ long byteOffset = (long)((uint8_t*)p - dataStart);
+ if ( (byteOffset % 4) != 0 ) {
+ _diagnostics.error("pointer not 4-byte aligned in DATA offset 0x%08lX\n", byteOffset);
+ free(bitmap);
+ return;
+ }
+ long boolIndex = byteOffset / 4;
+ // work around <rdar://24941083> by ignoring pointers to be slid that are NULL on disk
+ if ( *((pint_t*)p) == 0 ) {
+ std::string dylibName;
+ std::string segName;
+ findDylibAndSegment(p, dylibName, segName);
+ _diagnostics.warning("NULL pointer asked to be slid in %s at DATA region offset 0x%04lX of %s", segName.c_str(), byteOffset, dylibName.c_str());
+ continue;
+ }
+ bitmap[boolIndex] = true;
+ }
+
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)((uint8_t*)_buffer + _slideInfoFileOffset);
+ info->version = 2;
+ info->page_size = pageSize;
+ info->delta_mask = _archLayout->pointerDeltaMask;
+ info->value_add = (sizeof(pint_t) == 8) ? 0 : _archLayout->sharedMemoryStart; // only value_add for 32-bit archs
+
+ // 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);
+ if ( _diagnostics.hasError() ) {
+ free(bitmap);
+ return;
+ }
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+ free((void*)bitmap);
+
+ // fill in computed info
+ info->page_starts_offset = sizeof(dyld_cache_slide_info2);
+ info->page_starts_count = (unsigned)pageStarts.size();
+ info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info2)+pageStarts.size()*sizeof(uint16_t));
+ info->page_extras_count = (unsigned)pageExtras.size();
+ uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
+ uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
+ for (unsigned i=0; i < pageStarts.size(); ++i)
+ pageStartsBuffer[i] = pageStarts[i];
+ for (unsigned i=0; i < pageExtras.size(); ++i)
+ pageExtrasBuffer[i] = pageExtras[i];
+ // update header with final size
+ _buffer->header.slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+ if ( _buffer->header.slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info overflow buffer");
+ }
+ //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size());
+}
+
+
+/*
+void CacheBuilder::writeSlideInfoV1()
+{
+ // build one 128-byte bitmap per page (4096) of DATA
+ uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
+ uint8_t* const dataEnd = dataStart + regions[1].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;
+ dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
+ slideInfo->version = 1;
+ slideInfo->toc_offset = sizeof(dyld_cache_slide_info);
+ slideInfo->toc_count = toc_count;
+ slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128);
+ slideInfo->entries_count = 0;
+ slideInfo->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->entries_count = entry_count;
+ ::free((void*)bitmap);
+
+ _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
+}
+
+*/
+
+void CacheBuilder::fipsSign() {
+ __block bool found = false;
+ _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+ __block void *hash_location = nullptr;
+ // Return if this is not corecrypto
+ if (strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") != 0) {
+ return;
+ }
+ found = true;
+ auto parser = dyld3::MachOParser(mh, true);
+ parser.forEachLocalSymbol(_diagnostics, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+ if (strcmp(symbolName, "_fipspost_precalc_hmac") != 0)
+ return;
+ hash_location = (void *)(n_value - _archLayout->sharedMemoryStart + (uintptr_t)_buffer);
+ stop = true;
+ });
+
+ // Bail out if we did not find the symbol
+ if (hash_location == nullptr) {
+ _diagnostics.warning("Could not find _fipspost_precalc_hmac, skipping FIPS sealing");
+ return;
+ }
+
+ parser.forEachSection(^(const char *segName, const char *sectionName, uint32_t flags, const void *content, size_t size, bool illegalSectionSize, bool &stop) {
+ // FIXME: If we ever implement userspace __TEXT_EXEC this will need to be updated
+ if ( (strcmp(segName, "__TEXT" ) != 0) || (strcmp(sectionName, "__text") != 0) ) {
+ return;
+ }
+
+ if (illegalSectionSize) {
+ _diagnostics.error("FIPS section %s/%s extends beyond the end of the segment", segName, sectionName);
+ return;
+ }
+
+ //We have _fipspost_precalc_hmac and __TEXT,__text, seal it
+ unsigned char hmac_key = 0;
+ CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, content, size, hash_location);
+ stop = true;
+ });
+ });
+
+ if (!found) {
+ _diagnostics.warning("Could not find /usr/lib/system/libcorecrypto.dylib, skipping FIPS sealing");
+ }
+}
+
+void CacheBuilder::codeSign()
+{
+ uint8_t dscHashType;
+ uint8_t dscHashSize;
+ uint32_t dscDigestFormat;
+ bool agile = false;
+
+ // select which codesigning hash
+ switch (_options.codeSigningDigestMode) {
+ case DyldSharedCache::Agile:
+ agile = true;
+ // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
+ case DyldSharedCache::SHA1only:
+ dscHashType = CS_HASHTYPE_SHA1;
+ dscHashSize = CS_HASH_SIZE_SHA1;
+ dscDigestFormat = kCCDigestSHA1;
+ break;
+ case DyldSharedCache::SHA256only:
+ dscHashType = CS_HASHTYPE_SHA256;
+ dscHashSize = CS_HASH_SIZE_SHA256;
+ dscDigestFormat = kCCDigestSHA256;
+ break;
+ default:
+ _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
+ _options.codeSigningDigestMode);
+ return;
+ }
+
+ std::string cacheIdentifier = "com.apple.dyld.cache." + _options.archName;
+ if ( _options.dylibsRemovedDuringMastering ) {
+ if ( _options.optimizeStubs )
+ cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".release";
+ else
+ cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".development";
+ }
+ // get pointers into shared cache buffer
+ size_t inBbufferSize = _currentFileSize;
+ const uint8_t* inBuffer = (uint8_t*)_buffer;
+ uint8_t* csBuffer = (uint8_t*)_buffer+inBbufferSize;
+
+ // layout code signature contents
+ uint32_t blobCount = agile ? 4 : 3;
+ 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 idOffset = offsetof(CS_CodeDirectory, end_withExecSeg);
+ size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount;
+ size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
+ size_t cdSize = hashOffset + (slotCount * dscHashSize);
+ size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
+ size_t reqsSize = 12;
+ size_t cmsSize = sizeof(CS_Blob);
+ size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
+ size_t cd256Offset = cdOffset + cdSize;
+ size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
+ size_t cmsOffset = reqsOffset + reqsSize;
+ size_t sbSize = cmsOffset + cmsSize;
+ size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned
+
+ if ( _currentFileSize+sigSize > _allocatedBufferSize ) {
+ _diagnostics.error("cache buffer too small to hold code signature (buffer size=%lldMB, signature size=%ldMB, free space=%lldMB)",
+ _allocatedBufferSize/1024/1024, sigSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+ return;
+ }
+
+ // 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(blobCount);
+ 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);
+ if ( agile ) {
+ sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
+ sb->index[3].offset = htonl(cd256Offset);
+ }
+
+ // 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;
+
+ // 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(0x20400); // supports exec segment
+ 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 = 0; // not supported anymore
+ cd->teamOffset = 0; // no team ID
+ cd->spare3 = 0; // unused (must be zero)
+ cd->codeLimit64 = 0; // falls back to codeLimit
+
+ // executable segment info
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ cd->execSegBase = htonll(mappings[0].fileOffset); // base of TEXT segment
+ cd->execSegLimit = htonll(mappings[0].size); // size of TEXT segment
+ cd->execSegFlags = 0; // not a main binary
+
+ // initialize dynamic fields of Code Directory
+ strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
+
+ // 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);
+
+ CS_CodeDirectory* cd256;
+ uint8_t* hash256Slot;
+ uint8_t* reqsHash256Slot;
+ if ( agile ) {
+ // Note that the assumption here is that the size up to the hashes is the same as for
+ // sha1 code directory, and that they come last, after everything else.
+
+ cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
+ cd256->magic = htonl(CSMAGIC_CODEDIRECTORY);
+ cd256->length = htonl(cd256Size);
+ cd256->version = htonl(0x20400); // supports exec segment
+ cd256->flags = htonl(kSecCodeSignatureAdhoc);
+ cd256->hashOffset = htonl(hash256Offset);
+ cd256->identOffset = htonl(idOffset);
+ cd256->nSpecialSlots = htonl(xSlotCount);
+ cd256->nCodeSlots = htonl(slotCount);
+ cd256->codeLimit = htonl(inBbufferSize);
+ cd256->hashSize = CS_HASH_SIZE_SHA256;
+ cd256->hashType = CS_HASHTYPE_SHA256;
+ cd256->platform = 0; // not platform binary
+ cd256->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE);
+ cd256->spare2 = 0; // unused (must be zero)
+ cd256->scatterOffset = 0; // not supported anymore
+ cd256->teamOffset = 0; // no team ID
+ cd256->spare3 = 0; // unused (must be zero)
+ cd256->codeLimit64 = 0; // falls back to codeLimit
+
+ // executable segment info
+ cd256->execSegBase = cd->execSegBase;
+ cd256->execSegLimit = cd->execSegLimit;
+ cd256->execSegFlags = cd->execSegFlags;
+
+ // initialize dynamic fields of Code Directory
+ strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
+
+ // add special slot hashes
+ hash256Slot = (uint8_t*)cd256 + hash256Offset;
+ reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
+ CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
+ }
+ else {
+ cd256 = NULL;
+ hash256Slot = NULL;
+ reqsHash256Slot = NULL;
+ }
+
+ // 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));
+
+ // alter header of cache to record size and location of code signature
+ // do this *before* hashing each page
+ _buffer->header.codeSignatureOffset = inBbufferSize;
+ _buffer->header.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;
+
+ if ( agile ) {
+ CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot);
+ hash256Slot += CS_HASH_SIZE_SHA256;
+ }
+ code += CS_PAGE_SIZE;
+ }
+
+ // hash of entire code directory (cdHash) uses same 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(_cdHashFirst, fullCdHash, 20);
+ if ( agile ) {
+ uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
+ CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
+ // Note: cdHash is defined as first 20 bytes of hash, even for sha256
+ memcpy(_cdHashSecond, fullCdHash256, 20);
+ }
+ else {
+ memset(_cdHashSecond, 0, 20);
+ }
+
+ // increase file size to include newly append code signature
+ _currentFileSize += sigSize;
+}
+
+const bool CacheBuilder::agileSignature()
+{
+ return _options.codeSigningDigestMode == DyldSharedCache::Agile;
+}
+
+static const std::string cdHash(uint8_t hash[20])
+{
+ char buff[48];
+ for (int i = 0; i < 20; ++i)
+ sprintf(&buff[2*i], "%2.2x", hash[i]);
+ return buff;
+}
+
+const std::string CacheBuilder::cdHashFirst()
+{
+ return cdHash(_cdHashFirst);
+}
+
+const std::string CacheBuilder::cdHashSecond()
+{
+ return cdHash(_cdHashSecond);
+}
+
+void CacheBuilder::addCachedDylibsImageGroup(dyld3::ImageProxyGroup* dylibGroup)
+{
+ const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = dylibGroup->makeImageGroupBinary(_diagnostics, _s_neverStubEliminate);
+ if (!groupBinary)
+ return;
+
+ dyld3::launch_cache::ImageGroup group(groupBinary);
+ size_t groupSize = group.size();
+
+ if ( _currentFileSize+groupSize > _allocatedBufferSize ) {
+ _diagnostics.error("cache buffer too small to hold group[0] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)",
+ _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+ return;
+ }
+
+ // append ImageGroup data to read-only region of cache
+ uint8_t* loc = (uint8_t*)_buffer + _currentFileSize;
+ memcpy(loc, groupBinary, groupSize);
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ _buffer->header.dylibsImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+ _buffer->header.dylibsImageGroupSize = (uint32_t)groupSize;
+ _currentFileSize += groupSize;
+ free((void*)groupBinary);
+}
+
+
+void CacheBuilder::addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup* otherGroup)
+{
+ const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = otherGroup->makeImageGroupBinary(_diagnostics);
+ if (!groupBinary)
+ return;
+
+ dyld3::launch_cache::ImageGroup group(groupBinary);
+ size_t groupSize = group.size();
+
+ if ( _currentFileSize+groupSize > _allocatedBufferSize ) {
+ _diagnostics.error("cache buffer too small to hold group[1] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)",
+ _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+ return;
+ }
+
+ // append ImageGroup data to read-only region of cache
+ uint8_t* loc = (uint8_t*)_buffer + _currentFileSize;
+ memcpy(loc, groupBinary, groupSize);
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ _buffer->header.otherImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+ _buffer->header.otherImageGroupSize = (uint32_t)groupSize;
+ _currentFileSize += groupSize;
+ free((void*)groupBinary);
+}
+
+void CacheBuilder::addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures)
+{
+ // preflight space needed
+ size_t closuresSpace = 0;
+ for (const auto& entry : closures) {
+ dyld3::launch_cache::Closure closure(entry.second);
+ closuresSpace += closure.size();
+ }
+ size_t freeSpace = _allocatedBufferSize - _currentFileSize;
+ if ( closuresSpace > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
+ return;
+ }
+
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+ _buffer->header.progClosuresAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+ uint8_t* closuresBase = (uint8_t*)_buffer + _currentFileSize;
+ std::vector<DylibIndexTrie::Entry> closureEntrys;
+ uint32_t currentClosureOffset = 0;
+ for (const auto& entry : closures) {
+ const dyld3::launch_cache::binary_format::Closure* closBuf = entry.second;
+ closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
+ dyld3::launch_cache::Closure closure(closBuf);
+ size_t size = closure.size();
+ assert((size % 4) == 0);
+ memcpy(closuresBase+currentClosureOffset, closBuf, size);
+ currentClosureOffset += size;
+ freeSpace -= size;
+ free((void*)closBuf);
+ }
+ _buffer->header.progClosuresSize = currentClosureOffset;
+ _currentFileSize += currentClosureOffset;
+ freeSpace = _allocatedBufferSize - _currentFileSize;
+
+ // build trie of indexes into closures list
+ DylibIndexTrie closureTrie(closureEntrys);
+ std::vector<uint8_t> trieBytes;
+ closureTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 8) != 0 )
+ trieBytes.push_back(0);
+ if ( trieBytes.size() > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
+ return;
+ }
+ memcpy((uint8_t*)_buffer + _currentFileSize, &trieBytes[0], trieBytes.size());
+ _buffer->header.progClosuresTrieAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+ _buffer->header.progClosuresTrieSize = trieBytes.size();
+ _currentFileSize += trieBytes.size();
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 CacheBuilder_h
+#define CacheBuilder_h
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "ImageProxy.h"
+
+
+namespace dyld3 {
+ namespace launch_cache {
+ namespace binary_format {
+ struct ImageGroup;
+ struct Closure;
+ }
+ }
+}
+
+
+struct CacheBuilder {
+
+ CacheBuilder(const DyldSharedCache::CreateOptions& options);
+
+ bool build(const std::vector<DyldSharedCache::MappedMachO>& dylibsToCache,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables);
+ void deleteBuffer();
+ const DyldSharedCache* buffer() { return _buffer; }
+ size_t bufferSize() { return (size_t)_allocatedBufferSize; }
+ std::string errorMessage();
+ const std::set<std::string> warnings();
+ const bool agileSignature();
+ const std::string cdHashFirst();
+ const std::string cdHashSecond();
+
+ struct SegmentMappingInfo {
+ const void* srcSegment;
+ const char* segName;
+ uint64_t dstCacheAddress;
+ uint32_t dstCacheOffset;
+ uint32_t dstCacheSegmentSize;
+ uint32_t copySegmentSize;
+ uint32_t srcSegmentIndex;
+ };
+
+private:
+
+ typedef std::unordered_map<const mach_header*, std::vector<SegmentMappingInfo>> SegmentMapping;
+
+ struct ArchLayout
+ {
+ uint64_t sharedMemoryStart;
+ uint64_t sharedMemorySize;
+ uint64_t sharedRegionPadding;
+ uint64_t pointerDeltaMask;
+ const char* archName;
+ uint32_t branchPoolTextSize;
+ uint32_t branchPoolLinkEditSize;
+ uint32_t branchReach;
+ uint8_t sharedRegionAlignP2;
+ bool sharedRegionsAreDiscontiguous;
+ bool is64;
+ };
+
+ static const ArchLayout _s_archLayout[];
+ static const char* const _s_neverStubEliminate[];
+
+ std::vector<DyldSharedCache::MappedMachO> makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
+
+ SegmentMapping assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, struct dyld_cache_mapping_info regions[3]);
+
+ bool cacheOverflow(const dyld_cache_mapping_info regions[3]);
+ void adjustImageForNewSegmentLocations(const std::vector<uint64_t>& segNewStartAddresses,
+ const std::vector<uint64_t>& segCacheFileOffsets,
+ const std::vector<uint64_t>& segCacheSizes, std::vector<void*>& pointersForASLR);
+
+ void fipsSign();
+ void codeSign();
+ uint64_t pathHash(const char* path);
+ void writeCacheHeader(const struct dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping&);
+ void copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping);
+ void adjustAllImagesForNewSegmentLocations(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping);
+ void bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3]);
+ void writeSlideInfoV1();
+ void recomputeCacheUUID(void);
+ void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+
+ void addCachedDylibsImageGroup(dyld3::ImageProxyGroup*);
+ void addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup*);
+ void addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures);
+
+ template <typename P> void writeSlideInfoV2();
+ template <typename P> bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+ template <typename P> void addPageStarts(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+
+ const DyldSharedCache::CreateOptions& _options;
+ DyldSharedCache* _buffer;
+ Diagnostics _diagnostics;
+ const ArchLayout* _archLayout;
+ uint32_t _aliasCount;
+ uint64_t _slideInfoFileOffset;
+ uint64_t _slideInfoBufferSizeAllocated;
+ uint64_t _allocatedBufferSize;
+ uint64_t _currentFileSize;
+ uint64_t _vmSize;
+ std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
+ std::vector<void*> _pointersForASLR;
+ dyld3::ImageProxyGroup::PatchTable _patchTable;
+ std::vector<uint64_t> _branchPoolStarts;
+ uint64_t _branchPoolsLinkEditStartAddr;
+ uint8_t _cdHashFirst[20];
+ uint8_t _cdHashSecond[20];
+};
+
+
+// implemented in AdjustDylibSegemnts.cpp
+void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag);
+
+// implemented in OptimizerLinkedit.cpp
+uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo);
+
+// implemented in OptimizerBranches.cpp
+void bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const alwaysUsesStubsTo[], Diagnostics& diag);
+
+// implemented in OptimizerObjC.cpp
+void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag);
+
+
+
+inline uint64_t align(uint64_t addr, uint8_t p2)
+{
+ uint64_t mask = (1 << p2);
+ return (addr + mask - 1) & (-mask);
+}
+
+
+
+#endif /* CacheBuilder_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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/dyld_priv.h>
+#include <assert.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#if !DYLD_IN_PROCESS
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#endif
+
+#define NO_ULEB
+#include "MachOParser.h"
+#include "CacheBuilder.h"
+#include "DyldSharedCache.h"
+#include "LaunchCache.h"
+#include "Trie.hpp"
+#include "StringUtils.h"
+
+
+
+#if !DYLD_IN_PROCESS
+DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& options,
+ const std::vector<MappedMachO>& dylibsToCache,
+ const std::vector<MappedMachO>& otherOsDylibs,
+ const std::vector<MappedMachO>& osExecutables)
+{
+ CreateResults results;
+ CacheBuilder cache(options);
+
+ results.overflowed = cache.build(dylibsToCache, otherOsDylibs, osExecutables);
+
+ results.agileSignature = cache.agileSignature();
+ results.cdHashFirst = cache.cdHashFirst();
+ results.cdHashSecond = cache.cdHashSecond();
+ results.warnings = cache.warnings();
+ if ( cache.errorMessage().empty() ) {
+ results.cacheContent = cache.buffer();
+ results.cacheLength = cache.bufferSize();
+ }
+ else {
+ cache.deleteBuffer();
+ results.cacheContent = nullptr;
+ results.cacheLength = 0;
+ results.errorMessage = cache.errorMessage();
+ }
+ return results;
+}
+
+bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& rejected)
+{
+
+ // build map of dylibs
+ __block std::map<std::string, std::set<std::string>> badDylibs;
+ __block std::set<std::string> knownDylibs;
+ for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) {
+ std::set<std::string> reasons;
+ dyld3::MachOParser parser(dylib.mh);
+ if (parser.canBePlacedInDyldCache(dylib.runtimePath, reasons)) {
+ knownDylibs.insert(dylib.runtimePath);
+ knownDylibs.insert(parser.installName());
+ } else {
+ badDylibs[dylib.runtimePath] = reasons;
+ }
+ }
+
+ // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+ __block bool doAgain = true;
+ while ( doAgain ) {
+ __block std::vector<DyldSharedCache::MappedMachO> foundMappings;
+ doAgain = false;
+ // scan dylib list making sure all dependents are in dylib list
+ for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.runtimePath) != 0 )
+ continue;
+ dyld3::MachOParser parser(dylib.mh);
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( knownDylibs.count(loadPath) == 0 ) {
+ doAgain = true;
+ MappedMachO foundMapping;
+ if ( badDylibs.count(loadPath) == 0 )
+ foundMapping = loader(loadPath);
+ if ( foundMapping.length == 0 ) {
+ std::string reason = std::string("Could not find dependency '") + loadPath +"'";
+ auto i = badDylibs.find(dylib.runtimePath);
+ if (i == badDylibs.end()) {
+ std::set<std::string> reasons;
+ reasons.insert(reason);
+ badDylibs[dylib.runtimePath] = reasons;
+ } else {
+ i->second.insert(reason);
+ }
+ knownDylibs.erase(dylib.runtimePath);
+ dyld3::MachOParser parserBad(dylib.mh);
+ knownDylibs.erase(parserBad.installName());
+ }
+ else {
+ dyld3::MachOParser foundParser(foundMapping.mh);
+ std::set<std::string> reasons;
+ if (foundParser.canBePlacedInDyldCache(foundParser.installName(), reasons)) {
+ foundMappings.push_back(foundMapping);
+ knownDylibs.insert(foundMapping.runtimePath);
+ knownDylibs.insert(foundParser.installName());
+ } else {
+ auto i = badDylibs.find(dylib.runtimePath);
+ if (i == badDylibs.end()) {
+ badDylibs[dylib.runtimePath] = reasons;
+ } else {
+ i->second.insert(reasons.begin(), reasons.end());
+ }
+ }
+ }
+ }
+ });
+ }
+ dylibsToCache.insert(dylibsToCache.end(), foundMappings.begin(), foundMappings.end());
+ // remove bad dylibs
+ const auto badDylibsCopy = badDylibs;
+ dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const DyldSharedCache::MappedMachO& dylib) {
+ auto i = badDylibsCopy.find(dylib.runtimePath);
+ if ( i != badDylibsCopy.end()) {
+ rejected.push_back(std::make_pair(dylib, i->second));
+ return true;
+ }
+ else {
+ return false;
+ }
+ }), dylibsToCache.end());
+ }
+
+ return badDylibs.empty();
+}
+#endif
+
+void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
+ for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
+ handler((char*)this + m->fileOffset, m->address, m->size, m->initProt);
+ }
+}
+
+void DyldSharedCache::forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const
+{
+ const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( mappings[0].fileOffset != 0 )
+ return;
+ uint64_t firstImageOffset = 0;
+ uint64_t firstRegionAddress = mappings[0].address;
+ for (uint32_t i=0; i < header.imagesCount; ++i) {
+ const char* dylibPath = (char*)this + dylibs[i].pathFileOffset;
+ uint64_t offset = dylibs[i].address - firstRegionAddress;
+ if ( firstImageOffset == 0 )
+ firstImageOffset = offset;
+ // skip over aliases
+ if ( dylibs[i].pathFileOffset < firstImageOffset)
+ continue;
+ const mach_header* mh = (mach_header*)((char*)this + offset);
+ handler(mh, dylibPath);
+ }
+}
+
+void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const
+{
+ const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( mappings[0].fileOffset != 0 )
+ return;
+ uint64_t firstImageOffset = 0;
+ uint64_t firstRegionAddress = mappings[0].address;
+ for (uint32_t i=0; i < header.imagesCount; ++i) {
+ const char* dylibPath = (char*)this + dylibs[i].pathFileOffset;
+ uint64_t offset = dylibs[i].address - firstRegionAddress;
+ if ( firstImageOffset == 0 )
+ firstImageOffset = offset;
+ // skip over aliases
+ if ( dylibs[i].pathFileOffset < firstImageOffset)
+ continue;
+ handler(dylibPath, dylibs[i].modTime, dylibs[i].inode);
+ }
+}
+
+void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const
+{
+ // check for old cache without imagesText array
+ if ( header.mappingOffset < 123 )
+ return;
+
+ // walk imageText table and call callback for each entry
+ const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)this + header.imagesTextOffset);
+ const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header.imagesTextCount];
+ for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
+ handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset);
+ }
+}
+
+
+std::string DyldSharedCache::archName() const
+{
+ const char* archSubString = ((char*)this) + 8;
+ while (*archSubString == ' ')
+ ++archSubString;
+ return archSubString;
+}
+
+
+uint32_t DyldSharedCache::platform() const
+{
+ return header.platform;
+}
+
+#if !DYLD_IN_PROCESS
+std::string DyldSharedCache::mapFile() const
+{
+ __block std::string result;
+ __block std::vector<uint64_t> regionStartAddresses;
+ __block std::vector<uint64_t> regionSizes;
+ __block std::vector<uint64_t> regionFileOffsets;
+
+ result.reserve(256*1024);
+ forEachRegion(^(const 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*)this);
+ char lineBuffer[256];
+ 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 )
+ sprintf(lineBuffer, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
+ else
+ sprintf(lineBuffer, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size);
+ result += lineBuffer;
+ });
+
+ // TODO: add linkedit breakdown
+ result += "\n\n";
+
+ forEachImage(^(const mach_header* mh, const char* installName) {
+ result += std::string(installName) + "\n";
+ dyld3::MachOParser parser(mh);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ char lineBuffer[256];
+ sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", segName, vmAddr, vmAddr+vmSize);
+ result += lineBuffer;
+ });
+ result += "\n";
+ });
+
+ return result;
+}
+#endif
+
+
+uint64_t DyldSharedCache::unslidLoadAddress() const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ return mappings[0].address;
+}
+
+void DyldSharedCache::getUUID(uuid_t uuid) const
+{
+ memcpy(uuid, header.uuid, sizeof(uuid_t));
+}
+
+uint64_t DyldSharedCache::mappedSize() const
+{
+ __block uint64_t startAddr = 0;
+ __block uint64_t endAddr = 0;
+ forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if ( startAddr == 0 )
+ startAddr = vmAddr;
+ uint64_t end = vmAddr+size;
+ if ( end > endAddr )
+ endAddr = end;
+ });
+ return (endAddr - startAddr);
+}
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 DyldSharedCache_h
+#define DyldSharedCache_h
+
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "dyld_cache_format.h"
+#include "Diagnostics.h"
+#include "MachOParser.h"
+
+
+namespace dyld3 {
+ namespace launch_cache {
+ namespace binary_format {
+ struct Closure;
+ struct ImageGroup;
+ struct Image;
+ }
+ }
+}
+
+
+class VIS_HIDDEN DyldSharedCache
+{
+public:
+
+ enum CodeSigningDigestMode
+ {
+ SHA256only = 0,
+ SHA1only = 1,
+ Agile = 2
+ };
+
+ struct CreateOptions
+ {
+ std::string archName;
+ dyld3::Platform platform;
+ bool excludeLocalSymbols;
+ bool optimizeStubs;
+ bool optimizeObjC;
+ CodeSigningDigestMode codeSigningDigestMode;
+ bool agileSignatureChooseSHA256CdHash;
+ bool dylibsRemovedDuringMastering;
+ bool inodesAreSameAsRuntime;
+ bool cacheSupportsASLR;
+ bool forSimulator;
+ bool verbose;
+ bool evictLeafDylibsOnOverflow;
+ std::unordered_map<std::string, unsigned> dylibOrdering;
+ std::unordered_map<std::string, unsigned> dirtyDataSegmentOrdering;
+ std::vector<std::string> pathPrefixes;
+ std::string loggingPrefix;
+ };
+
+ struct MappedMachO
+ {
+ MappedMachO()
+ : mh(nullptr), length(0), isSetUID(false), protectedBySIP(false), sliceFileOffset(0), modTime(0), inode(0) { }
+ MappedMachO(const std::string& path, const mach_header* p, size_t l, bool isu, bool sip, uint64_t o, uint64_t m, uint64_t i)
+ : runtimePath(path), mh(p), length(l), isSetUID(isu), protectedBySIP(sip), sliceFileOffset(o), modTime(m), inode(i) { }
+
+ std::string runtimePath;
+ const mach_header* mh;
+ size_t length;
+ uint64_t isSetUID : 1,
+ protectedBySIP : 1,
+ sliceFileOffset : 62;
+ uint64_t modTime; // only recorded if inodesAreSameAsRuntime
+ uint64_t inode; // only recorded if inodesAreSameAsRuntime
+ };
+
+ struct CreateResults
+ {
+ const DyldSharedCache* cacheContent = nullptr; // caller needs to vm_deallocate() when done
+ size_t cacheLength = 0;
+ std::string errorMessage;
+ std::set<std::string> warnings;
+ bool agileSignature = false;
+ std::string cdHashFirst;
+ std::string cdHashSecond;
+ bool overflowed = false;
+ };
+
+
+ // This function verifies the set of dylibs that will go into the cache are self contained. That the depend on no dylibs
+ // outset the set. It will call back the loader function to try to find any mising dylibs.
+ static bool verifySelfContained(std::vector<MappedMachO>& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& excluded);
+
+
+ //
+ // This function is single threaded and creates a shared cache. The cache file is created in-memory.
+ //
+ // Inputs:
+ // options: various per-platform flags
+ // dylibsToCache: a list of dylibs to include in the cache
+ // otherOsDylibs: a list of other OS dylibs and bundle which should have load info added to the cache
+ // osExecutables: a list of main executables which should have closures created in the cache
+ //
+ // Returns:
+ // On success:
+ // cacheContent: start of the allocated cache buffer which must be vm_deallocated after the caller writes out the buffer.
+ // cacheLength: size of the allocated cache buffer
+ // cdHash: hash of the code directory of the code blob of the created cache
+ // warnings: all warning messsages generated during the creation of the cache
+ //
+ // On failure:
+ // cacheContent: nullptr
+ // errorMessage: the string describing why the cache could not be created
+ // warnings: all warning messsages generated before the failure
+ //
+ static CreateResults create(const CreateOptions& options,
+ const std::vector<MappedMachO>& dylibsToCache,
+ const std::vector<MappedMachO>& otherOsDylibs,
+ const std::vector<MappedMachO>& osExecutables);
+
+
+ //
+ // Returns a text "map" file as a big string
+ //
+ std::string mapFile() const;
+
+
+ //
+ // Returns the architecture name of the shared cache, e.g. "arm64"
+ //
+ std::string archName() const;
+
+
+ //
+ // Returns the platform the cache is for
+ //
+ uint32_t platform() const;
+
+
+ //
+ // Iterates over each dylib in the cache
+ //
+ void forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const;
+
+
+ //
+ // Iterates over each dylib in the cache
+ //
+ void forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const;
+
+
+ //
+ // Iterates over each dylib in the cache
+ //
+ void forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const;
+
+
+ //
+ // Iterates over each of the three regions in the cache
+ //
+ void forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const;
+
+
+ //
+ // returns address the cache would load at if unslid
+ //
+ uint64_t unslidLoadAddress() const;
+
+
+ //
+ // returns UUID of cache
+ //
+ void getUUID(uuid_t uuid) const;
+
+
+ //
+ // returns the vm size required to map cache
+ //
+ uint64_t mappedSize() const;
+
+
+ dyld_cache_header header;
+};
+
+
+
+
+
+
+
+
+#endif /* DyldSharedCache_h */
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef __FILE_ABSTRACTION__
+#define __FILE_ABSTRACTION__
+
+
+#include <stdint.h>
+#include <string.h>
+#include <libkern/OSByteOrder.h>
+
+#ifdef __OPTIMIZE__
+#define INLINE __attribute__((always_inline))
+#else
+#define INLINE
+#endif
+
+//
+// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
+//
+// For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
+//
+//
+// get16() read a 16-bit number from an E endian struct
+// set16() write a 16-bit number to an E endian struct
+// get32() read a 32-bit number from an E endian struct
+// set32() write a 32-bit number to an E endian struct
+// get64() read a 64-bit number from an E endian struct
+// set64() write a 64-bit number to an E endian struct
+//
+// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+//
+// getBitsRaw() read a bit field from a struct with native endianness
+// setBitsRaw() write a bit field from a struct with native endianness
+//
+
+class BigEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
+
+ static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
+ static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << (32-firstBit-bitCount));
+ temp |= ((value & mask) << (32-firstBit-bitCount));
+ into = temp; }
+ enum { little_endian = 0 };
+};
+
+
+class LittleEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
+
+ static int32_t get32(const int32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
+ static void set32(int32_t& into, int32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << firstBit);
+ temp |= ((value & mask) << firstBit);
+ into = temp; }
+ enum { little_endian = 1 };
+};
+
+
+template <typename _E>
+class Pointer32
+{
+public:
+ typedef uint32_t uint_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); }
+
+ // Round to a P-size boundary
+ template <typename T>
+ static T round_up(T value) { return (value+3) & ~(T)3; }
+ template <typename T>
+ static T round_down(T value) { return value & ~(T)3; }
+};
+
+
+template <typename _E>
+class Pointer64
+{
+public:
+ typedef uint64_t uint_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
+
+ // Round to a P-size boundary
+ template <typename T>
+ static T round_up(T value) { return (value+7) & ~(T)7; }
+ template <typename T>
+ static T round_down(T value) { return value & ~(T)7; }
+};
+
+
+
+
+
+#endif // __FILE_ABSTRACTION__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <dispatch/dispatch.h>
+#include <mach-o/dyld.h>
+#include <System/sys/csr.h>
+#include <rootless.h>
+
+#include <string>
+#include <fstream>
+#include <sstream>
+
+#include "FileUtils.h"
+#include "Diagnostics.h"
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
+#endif
+
+
+void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& path), void (^fileCallback)(const std::string& path, const struct stat&), bool processFiles)
+{
+ std::string fullDirPath = pathPrefix + path;
+ DIR* dir = ::opendir(fullDirPath.c_str());
+ if ( dir == nullptr ) {
+ //fprintf(stderr, "can't read 'dir '%s', errno=%d\n", inputPath.c_str(), errno);
+ return;
+ }
+ while (dirent* entry = readdir(dir)) {
+ struct stat statBuf;
+ std::string dirAndFile = path + "/" + entry->d_name;
+ std::string fullDirAndFile = pathPrefix + dirAndFile;
+ switch ( entry->d_type ) {
+ case DT_REG:
+ if ( processFiles ) {
+ if ( ::lstat(fullDirAndFile.c_str(), &statBuf) == -1 )
+ break;
+ if ( ! S_ISREG(statBuf.st_mode) )
+ break;
+ fileCallback(dirAndFile, statBuf);
+ }
+ break;
+ case DT_DIR:
+ if ( strcmp(entry->d_name, ".") == 0 )
+ break;
+ if ( strcmp(entry->d_name, "..") == 0 )
+ break;
+ if ( dirFilter(dirAndFile) )
+ break;
+ iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
+ break;
+ case DT_LNK:
+ // don't follow symlinks, dylib will be found through absolute path
+ break;
+ }
+ }
+ ::closedir(dir);
+}
+
+
+bool safeSave(const void* buffer, size_t bufferLen, const std::string& path)
+{
+ std::string pathTemplate = path + "-XXXXXX";
+ size_t templateLen = strlen(pathTemplate.c_str())+2;
+ char pathTemplateSpace[templateLen];
+ strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
+ int fd = mkstemp(pathTemplateSpace);
+ if ( fd != -1 ) {
+ ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
+ if ( writtenSize == bufferLen ) {
+ ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--"
+ if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
+ ::close(fd);
+ return true; // success
+ }
+ }
+ ::close(fd);
+ ::unlink(pathTemplateSpace);
+ }
+ return false; // failure
+}
+
+const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
+{
+ struct stat statBuf;
+ if ( ::stat(path.c_str(), &statBuf) != 0 )
+ return nullptr;
+
+ int fd = ::open(path.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return nullptr;
+
+ const void *p = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ ::close(fd);
+ if ( p != MAP_FAILED ) {
+ mappedSize = (size_t)statBuf.st_size;
+ return p;
+ }
+
+ return nullptr;
+}
+
+static bool sipIsEnabled()
+{
+ static bool rootlessEnabled;
+ static dispatch_once_t onceToken;
+ // Check to make sure file system protections are on at all
+ dispatch_once(&onceToken, ^{
+ rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0);
+ });
+ return rootlessEnabled;
+}
+
+bool isProtectedBySIP(const std::string& path)
+{
+ if ( !sipIsEnabled() )
+ return false;
+
+ return (rootless_check_trusted(path.c_str()) == 0);
+}
+
+bool isProtectedBySIP(int fd)
+{
+ if ( !sipIsEnabled() )
+ return false;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ return (rootless_check_trusted_fd(fd) == 0);
+#else
+ // fallback to using rootless_check_trusted
+ char realPath[MAXPATHLEN];
+ if ( fcntl(fd, F_GETPATH, realPath) == 0 )
+ return (rootless_check_trusted(realPath) == 0);
+ return false;
+#endif
+}
+
+bool fileExists(const std::string& path)
+{
+ struct stat statBuf;
+ return ( ::stat(path.c_str(), &statBuf) == 0 );
+}
+
+// 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.
+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();
+ }
+
+ return order;
+}
+
+
+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 basePath(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 realPath(const std::string& path)
+{
+ char resolvedPath[PATH_MAX];
+ if (realpath(dirPath(path).c_str(), &resolvedPath[0]) != nullptr) {
+ return std::string(resolvedPath) + "/" + basePath(path);
+ } else {
+ return "";
+ }
+}
+
+std::string realFilePath(const std::string& path)
+{
+ char resolvedPath[PATH_MAX];
+ if ( realpath(path.c_str(), resolvedPath) != nullptr )
+ return std::string(resolvedPath);
+ else
+ return "";
+}
+
+
+std::string normalize_absolute_file_path(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;
+}
+
+
+#if BUILDING_CACHE_BUILDER
+
+FileCache fileCache;
+
+FileCache::FileCache(void)
+{
+ cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
+}
+
+void FileCache::preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths)
+{
+ for (auto& path : paths) {
+ preflightCache(diags, path);
+ }
+}
+
+void FileCache::preflightCache(Diagnostics& diags, const std::string& path)
+{
+ dispatch_async(cache_queue, ^{
+ std::string normalizedPath = normalize_absolute_file_path(path);
+ if (entries.count(normalizedPath) == 0) {
+ entries[normalizedPath] = fill(diags, normalizedPath);
+ }
+ });
+}
+
+std::pair<uint8_t*, struct stat> FileCache::cacheLoad(Diagnostics& diags, const std::string path)
+{
+ __block bool found = false;
+ __block std::pair<uint8_t*, struct stat> retval;
+ std::string normalizedPath = normalize_absolute_file_path(path);
+ dispatch_sync(cache_queue, ^{
+ auto entry = entries.find(normalizedPath);
+ if (entry != entries.end()) {
+ retval = entry->second;
+ found = true;
+ }
+ });
+
+ if (!found) {
+ auto info = fill(diags, normalizedPath);
+ dispatch_sync(cache_queue, ^{
+ auto entry = entries.find(normalizedPath);
+ if (entry != entries.end()) {
+ retval = entry->second;
+ } else {
+ retval = entries[normalizedPath] = info;
+ retval = info;
+ }
+ });
+ }
+
+ return retval;
+}
+
+//FIXME error handling
+std::pair<uint8_t*, struct stat> FileCache::fill(Diagnostics& diags, const std::string& path)
+{
+ void* buffer_ptr = nullptr;
+ struct stat stat_buf;
+ struct statfs statfs_buf;
+ bool localcopy = true;
+
+ int fd = ::open(path.c_str(), O_RDONLY, 0);
+ if (fd == -1) {
+ diags.verbose("can't open file '%s', errno=%d\n", path.c_str(), errno);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ }
+
+ if (fstat(fd, &stat_buf) == -1) {
+ diags.verbose("can't stat open file '%s', errno=%d\n", path.c_str(), errno);
+ ::close(fd);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ }
+
+ if (stat_buf.st_size < 4096) {
+ diags.verbose("file too small '%s'\n", path.c_str());
+ ::close(fd);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ }
+
+ if(fstatfs(fd, &statfs_buf) == 0) {
+ std::string fsName = statfs_buf.f_fstypename;
+ if (fsName == "hfs" || fsName == "apfs") {
+ localcopy = false;
+ }
+ }
+
+ if (!localcopy) {
+ buffer_ptr = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buffer_ptr == MAP_FAILED) {
+ diags.verbose("mmap() for file at %s failed, errno=%d\n", path.c_str(), errno);
+ ::close(fd);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ }
+ } else {
+ buffer_ptr = malloc((size_t)stat_buf.st_size);
+ ssize_t readBytes = pread(fd, buffer_ptr, (size_t)stat_buf.st_size, 0);
+ if (readBytes == -1) {
+ diags.verbose("Network read for file at %s failed, errno=%d\n", path.c_str(), errno);
+ ::close(fd);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ } else if (readBytes != stat_buf.st_size) {
+ diags.verbose("Network read udnerrun for file at %s, expected %lld bytes, got %zd bytes\n", path.c_str(), stat_buf.st_size, readBytes);
+ ::close(fd);
+ return std::make_pair((uint8_t*)(-1), stat_buf);
+ }
+ }
+
+ ::close(fd);
+
+ return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
+}
+
+#endif // BUILDING_CACHE_BUILDER
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 FileUtils_h
+#define FileUtils_h
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <dispatch/dispatch.h>
+
+class Diagnostics;
+
+#if BUILDING_CACHE_BUILDER
+struct FileCache {
+ FileCache(void);
+ std::pair<uint8_t*, struct stat> cacheLoad(Diagnostics& diags, const std::string path);
+ void preflightCache(Diagnostics& diags, const std::string& path);
+ void preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths);
+
+private:
+ std::pair<uint8_t*, struct stat> fill(Diagnostics& diags, const std::string& path);
+
+ std::unordered_map<std::string, std::pair<uint8_t*, struct stat>> entries;
+ dispatch_queue_t cache_queue;
+};
+
+extern FileCache fileCache;
+#endif
+
+//
+// recursively walk all files in a directory tree
+// symlinks are ignored
+// dirFilter should return true on directories which should not be recursed into
+// callback is called on each regular file found with stat() info about the file
+//
+void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& dirPath),
+ void (^callback)(const std::string& path, const struct stat& statBuf), bool processFiles=true);
+
+
+//
+// writes the buffer to a temp file, then renames the file to the final path
+// returns true on success
+//
+bool safeSave(const void* buffer, size_t bufferLen, const std::string& path);
+
+
+const void* mapFileReadOnly(const std::string& path, size_t& mappedSize);
+
+bool isProtectedBySIP(const std::string& path);
+bool isProtectedBySIP(int fd);
+
+bool fileExists(const std::string& path);
+
+std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile);
+
+std::string normalize_absolute_file_path(std::string path);
+std::string basePath(const std::string& path);
+std::string dirPath(const std::string& path);
+std::string realPath(const std::string& path);
+std::string realFilePath(const std::string& path);
+
+std::string toolDir();
+
+
+
+
+#endif // FileUtils_h
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <mach/mach_vm.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+#include <uuid/uuid.h>
+#include <os/log.h>
+
+#include <string>
+#include <vector>
+#include <array>
+
+#include "ImageProxy.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "MachOParser.h"
+#include "LaunchCacheFormat.h"
+#include "LaunchCacheWriter.h"
+#include "PathOverrides.h"
+#include "libdyldEntryVector.h"
+
+namespace dyld3 {
+
+typedef launch_cache::TargetSymbolValue TargetSymbolValue;
+
+
+
+/////////////////////////// ImageProxy ///////////////////////////
+
+ImageProxy::ImageProxy(const mach_header* mh, const BinaryImageData* imageData, uint32_t indexInGroup, bool dyldCacheIsRaw)
+ : _mh(mh), _sliceFileOffset(0), _modTime(0), _inode(0), _imageBinaryData(imageData), _runtimePath(launch_cache::Image(imageData).path()),
+ _groupNum(0), _indexInGroup(indexInGroup), _isSetUID(false), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(false), _overrideOf(ImageRef::weakImportMissing()),
+ _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
+ _invalid(launch_cache::Image(imageData).isInvalid()), _staticallyReferenced(false), _cwdMustBeThisDir(false)
+{
+}
+
+ImageProxy::ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw)
+ : _mh(mapping.mh), _sliceFileOffset(mapping.sliceFileOffset), _modTime(mapping.modTime), _inode(mapping.inode), _imageBinaryData(nullptr), _runtimePath(mapping.runtimePath),
+ _groupNum(groupNum), _indexInGroup(indexInGroup), _isSetUID(mapping.isSetUID), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(mapping.protectedBySIP),
+ _overrideOf(ImageRef::weakImportMissing()), _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
+ _invalid(false), _staticallyReferenced(false), _cwdMustBeThisDir(false)
+{
+}
+
+
+void ImageProxy::processRPaths(ImageProxyGroup& owningGroup)
+{
+ // parse LC_RPATH
+ __block std::unordered_set<std::string> rawRpaths;
+ MachOParser parser(_mh, _dyldCacheIsRaw);
+ parser.forEachRPath(^(const char* rpath, bool& stop) {
+ if ( rawRpaths.count(rpath) ) {
+ _diag.warning("duplicate LC_RPATH (%s) in %s", rpath, _runtimePath.c_str());
+ return;
+ }
+ rawRpaths.insert(rpath);
+ std::string thisRPath = rpath;
+ if ( startsWith(thisRPath, "@executable_path/") ) {
+ std::string mainPath = owningGroup.mainProgRuntimePath();
+ if ( mainPath.empty() && parser.isDynamicExecutable() ) {
+ mainPath = _runtimePath;
+ }
+ if ( !mainPath.empty() ) {
+ std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1) + thisRPath.substr(17);
+ std::string normalizedPath = owningGroup.normalizedPath(newPath);
+ if ( fileExists(normalizedPath) )
+ _rpaths.push_back(normalizedPath);
+ else
+ _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str());
+ char resolvedMainPath[PATH_MAX];
+ if ( (realpath(mainPath.c_str(), resolvedMainPath) != nullptr) && (mainPath.c_str() != resolvedMainPath) ) {
+ std::string realMainPath = resolvedMainPath;
+ size_t lastSlashPos = realMainPath.rfind('/');
+ std::string newRealPath = realMainPath.substr(0, lastSlashPos+1) + thisRPath.substr(17);
+ if ( realMainPath != mainPath ) {
+ for (const std::string& pre : owningGroup._buildTimePrefixes) {
+ std::string aPath = owningGroup.normalizedPath(pre + newRealPath);
+ if ( fileExists(aPath) ) {
+ _rpaths.push_back(owningGroup.normalizedPath(newRealPath));
+ }
+ }
+ }
+ }
+ }
+ else {
+ _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
+ }
+ }
+ else if ( thisRPath == "@executable_path" ) {
+ std::string mainPath = owningGroup.mainProgRuntimePath();
+ if ( mainPath.empty() && parser.isDynamicExecutable() ) {
+ mainPath = _runtimePath;
+ }
+ if ( !mainPath.empty() ) {
+ std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1);
+ std::string normalizedPath = owningGroup.normalizedPath(newPath);
+ _rpaths.push_back(normalizedPath);
+ }
+ else {
+ _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
+ }
+ }
+ else if ( startsWith(thisRPath, "@loader_path/") ) {
+ size_t lastSlashPos = _runtimePath.rfind('/');
+ std::string newPath = _runtimePath.substr(0, lastSlashPos+1) + thisRPath.substr(13);
+ bool found = false;
+ for (const std::string& pre : owningGroup._buildTimePrefixes) {
+ std::string aPath = owningGroup.normalizedPath(pre + newPath);
+ if ( fileExists(aPath) ) {
+ _rpaths.push_back(owningGroup.normalizedPath(newPath));
+ found = true;
+ break;
+ }
+ }
+ char resolvedPath[PATH_MAX];
+ if ( (realpath(_runtimePath.c_str(), resolvedPath) != nullptr) && (_runtimePath.c_str() != resolvedPath) ) {
+ std::string realRunPath = resolvedPath;
+ lastSlashPos = realRunPath.rfind('/');
+ std::string newRealPath = realRunPath.substr(0, lastSlashPos+1) + thisRPath.substr(13);
+ if ( newRealPath != newPath ) {
+ for (const std::string& pre : owningGroup._buildTimePrefixes) {
+ std::string aPath = owningGroup.normalizedPath(pre + newRealPath);
+ if ( fileExists(aPath) ) {
+ _rpaths.push_back(owningGroup.normalizedPath(newRealPath));
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ if ( !found ) {
+ // even though this path does not exist, we need to add it to must-be-missing paths
+ // in case it shows up at launch time
+ _rpaths.push_back(owningGroup.normalizedPath(newPath));
+ _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str());
+ }
+ }
+ else if ( thisRPath == "@loader_path" ) {
+ size_t lastSlashPos = _runtimePath.rfind('/');
+ std::string newPath = _runtimePath.substr(0, lastSlashPos+1);
+ std::string normalizedPath = owningGroup.normalizedPath(newPath);
+ _rpaths.push_back(normalizedPath);
+ }
+ else if ( rpath[0] == '@' ) {
+ _diag.warning("LC_RPATH with unknown @ variable (%s) in %s", rpath, _runtimePath.c_str());
+ }
+ else {
+ if ( rpath[0] == '/' )
+ _diag.warning("LC_RPATH is absolute path (%s) in %s", rpath, _runtimePath.c_str());
+ _rpaths.push_back(rpath);
+ }
+ });
+ //if ( !_rpaths.empty() ) {
+ // fprintf(stderr, "for %s\n", _runtimePath.c_str());
+ // for (const std::string& p : _rpaths)
+ // fprintf(stderr, " %s\n", p.c_str());
+ //}
+}
+
+void ImageProxy::addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* prev, bool staticallyReferenced)
+{
+ // mark binaries that are statically referenced and thus will never be unloaded
+ if ( staticallyReferenced )
+ _staticallyReferenced = true;
+
+ if ( _deepDependentsSet )
+ return;
+
+ // find all immediate dependents
+ addDependentsShallow(owningGroup, prev);
+ if ( _diag.hasError() ) {
+ _invalid = true;
+ return;
+ }
+
+ // recurse though each dependent
+ RPathChain rchain = { this, prev, _rpaths };
+ for (ImageProxy* proxy : _dependents) {
+ if ( proxy == nullptr )
+ continue; // skip over weak missing dependents
+ if ( !proxy->_directDependentsSet )
+ proxy->addDependentsDeep(owningGroup, &rchain, staticallyReferenced);
+ if ( proxy->invalid() )
+ _invalid = true;
+ }
+
+ _deepDependentsSet = true;
+}
+
+void ImageProxy::addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* prev)
+{
+ if ( _directDependentsSet )
+ return;
+
+ MachOParser thisParser(mh(), _dyldCacheIsRaw);
+ dyld3::Platform thisPlatform = thisParser.platform();
+
+ processRPaths(owningGroup);
+ __block RPathChain rchain = { this, prev, _rpaths };
+
+ thisParser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ if ( (loadPath[0] != '/') && (loadPath[0] != '@') ) {
+ _diag.warning("load path is file system relative (%s) in %s", loadPath, runtimePath().c_str());
+ }
+ Diagnostics depDiag;
+ ImageProxy* dep = owningGroup.findImage(depDiag, loadPath, isWeak, &rchain);
+ if ( (dep == nullptr) || dep->invalid() ) {
+ if (isWeak) {
+ // weak link against a broken dylib, pretend dylib is not there
+ dep = nullptr;
+ } else {
+ if ( depDiag.warnings().empty() ) {
+ if ( thisParser.header()->filetype == MH_EXECUTE )
+ _diag.error("required dylib '%s' not found", loadPath);
+ else
+ _diag.error("required dylib '%s' not found, needed by '%s'", loadPath, runtimePath().c_str());
+ }
+ else {
+ std::string allTries;
+ for (const std::string& warn : depDiag.warnings()) {
+ if ( allTries.empty() )
+ allTries = warn;
+ else
+ allTries = allTries + ", " + warn;
+ }
+ _diag.error("required dylib '%s' not found, needed by '%s'. Did try: %s", loadPath, runtimePath().c_str(), allTries.c_str());
+ }
+ }
+ }
+ else {
+ MachOParser depParser(dep->mh(), _dyldCacheIsRaw);
+ if ( _diag.noError() ) {
+ // verify found image has compatible version and matching platform
+ dyld3::Platform depPlatform = depParser.platform();
+ if ( depPlatform != thisPlatform ) {
+ // simulator allows a few macOS libSystem dylibs
+ if ( !inLibSystem() || !dep->inLibSystem() ) {
+ _diag.error("found '%s' but it was built for different platform '%s' than required '%s'. Needed by '%s'", dep->runtimePath().c_str(),
+ MachOParser::platformName(depPlatform).c_str(), MachOParser::platformName(thisPlatform).c_str(), runtimePath().c_str());
+ }
+ }
+ }
+ if ( _diag.noError() ) {
+ // verify compat version
+ const char* installName;
+ uint32_t foundCompatVers;
+ uint32_t foundCurrentVers;
+ if ( depParser.header()->filetype != MH_DYLIB ) {
+ _diag.error("found '%s' which is not a dylib. Needed by '%s'", dep->runtimePath().c_str(), runtimePath().c_str());
+ }
+ else {
+ depParser.getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers);
+ if ( foundCompatVers < compatVersion ) {
+ _diag.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'", dep->runtimePath().c_str(),
+ MachOParser::versionString(foundCompatVers).c_str(), MachOParser::versionString(compatVersion).c_str(), runtimePath().c_str());
+ }
+ }
+ }
+ }
+ if ( _diag.hasError() ) {
+ stop = true;
+ _invalid = true;
+ }
+ _dependents.push_back(dep);
+ if ( isWeak )
+ _dependentsKind.push_back(launch_cache::Image::LinkKind::weak);
+ else if ( isReExport )
+ _dependentsKind.push_back(launch_cache::Image::LinkKind::reExport);
+ else if ( isUpward )
+ _dependentsKind.push_back(launch_cache::Image::LinkKind::upward);
+ else
+ _dependentsKind.push_back(launch_cache::Image::LinkKind::regular);
+ });
+ _directDependentsSet = true;
+}
+
+bool ImageProxy::inLibSystem() const
+{
+ return startsWith(runtimePath(), "/usr/lib/system/") || startsWith(runtimePath(), "/usr/lib/libSystem.");
+}
+
+void ImageProxy::forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const
+{
+ for (int i=0; i < _dependents.size(); ++i) {
+ handler(_dependents[i], _dependentsKind[i]);
+ }
+}
+
+
+bool ImageProxy::findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const
+{
+ MachOParser parser(_mh, _dyldCacheIsRaw);
+ return parser.findExportedSymbol(diag, symbolName, (void*)this, foundInfo, ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+ ImageProxy* proxy = (ImageProxy*)extra;
+ if ( depIndex < proxy->_dependents.size() ) {
+ ImageProxy* depProxy = proxy->_dependents[depIndex];
+ *foundMH = depProxy->_mh;
+ *foundExtra = (void*)depProxy;
+ return true;
+ }
+ return false;
+ });
+}
+
+bool ImageProxy::InitOrderInfo::beforeHas(ImageRef ref)
+{
+ ImageRef clearRef = ref;
+ clearRef.clearKind();
+ return ( std::find(initBefore.begin(), initBefore.end(), clearRef) != initBefore.end() );
+}
+
+bool ImageProxy::InitOrderInfo::upwardHas(ImageProxy* proxy)
+{
+ return ( std::find(danglingUpward.begin(), danglingUpward.end(), proxy) != danglingUpward.end() );
+}
+
+void ImageProxy::InitOrderInfo::removeRedundantUpwards()
+{
+ danglingUpward.erase(std::remove_if(danglingUpward.begin(), danglingUpward.end(),
+ [&](ImageProxy* proxy) {
+ ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup);
+ return beforeHas(ref);
+ }), danglingUpward.end());
+}
+
+
+//
+// Every image has a list of "init-before" which means if that image was dlopen()ed
+// here is the exact list of images to initialize in the exact order. This makes
+// the runtime easy. It just walks the init-before list in order and runs each
+// initializer if it has not already been run.
+//
+// The init-before list for each image is calculated based on the init-before list
+// of each of its dependents. It simply starts with the list of its first dependent,
+// then appends the list of the next, removing entries already in the list, etc.
+// Lastly if the current image has an initializer, it is appended to its init-before list.
+//
+// To handle cycles, when recursing to get a dependent's init-before list, any image
+// whose list is still being calculated (cycle), just returns its list so far.
+//
+// Explicit upward links are handled in two parts. First, in the algorithm described above,
+// all upward links are ignored, which works fine as long as anything upward linked is
+// downward linked at some point. If not, it is called a "dangling upward link". Since
+// nothing depends on those, they are added to the end of the final init-before list.
+//
+
+void ImageProxy::recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup)
+{
+ if ( _initBeforesComputed )
+ return;
+ _initBeforesComputed = true; // break cycles
+
+ if ( _imageBinaryData != nullptr ) {
+ assert(_groupNum == 0);
+ // if this is proxy for something in dyld cache, get its list from cache
+ // and parse list into befores and upwards
+ launch_cache::Image image(_imageBinaryData);
+ image.forEachInitBefore(^(launch_cache::binary_format::ImageRef ref) {
+ if ( (LinkKind)ref.kind() == LinkKind::upward ) {
+ ImageProxyGroup* groupP = &owningGroup;
+ while (groupP->_groupNum != 0)
+ groupP = groupP->_nextSearchGroup;
+ launch_cache::ImageGroup dyldCacheGroup(groupP->_basedOn);
+ launch_cache::Image dyldCacheImage = dyldCacheGroup.image(ref.indexInGroup());
+ Diagnostics diag;
+ ImageProxy* p = groupP->findAbsoluteImage(diag, dyldCacheImage.path(), false, false);
+ if ( diag.noError() )
+ _initBeforesInfo.danglingUpward.push_back(p);
+ }
+ else {
+ _initBeforesInfo.initBefore.push_back(ref);
+ }
+ });
+ }
+ else {
+ // calculate init-before list for this image by merging init-before's of all its dependent dylibs
+ unsigned depIndex = 0;
+ for (ImageProxy* depProxy : _dependents) {
+ if ( depProxy == nullptr ) {
+ assert(_dependentsKind[depIndex] == LinkKind::weak);
+ }
+ else {
+ if ( _dependentsKind[depIndex] == LinkKind::upward ) {
+ // if this upward link is already in the list, we ignore it. Otherwise add to front of list
+ if ( _initBeforesInfo.upwardHas(depProxy) ) {
+ // already in upward list, do nothing
+ }
+ else {
+ ImageRef ref(0, depProxy->_groupNum, depProxy->_indexInGroup);
+ if ( _initBeforesInfo.beforeHas(ref) ) {
+ // already in before list, do nothing
+ }
+ else {
+ // add to upward list
+ _initBeforesInfo.danglingUpward.push_back(depProxy);
+ }
+ }
+ }
+ else {
+ // compute init-befores of downward dependents
+ depProxy->recursiveBuildInitBeforeInfo(owningGroup);
+ // merge befores from this downward link into current befores list
+ for (ImageRef depInit : depProxy->_initBeforesInfo.initBefore) {
+ if ( !_initBeforesInfo.beforeHas(depInit) )
+ _initBeforesInfo.initBefore.push_back(depInit);
+ }
+ // merge upwards from this downward link into current befores list
+ for (ImageProxy* upProxy : depProxy->_initBeforesInfo.danglingUpward) {
+ ImageRef ref(0, upProxy->_groupNum, upProxy->_indexInGroup);
+ if ( _initBeforesInfo.beforeHas(ref) ) {
+ // already in current initBefore list, so ignore this upward
+ }
+ else if ( _initBeforesInfo.upwardHas(upProxy) ) {
+ // already in current danglingUpward list, so ignore this upward
+ }
+ else {
+ // append to current danglingUpward list
+ _initBeforesInfo.danglingUpward.push_back(upProxy);
+ }
+ }
+ }
+ }
+ ++depIndex;
+ }
+ // eliminate any upward links added to befores list by some other dependent
+ _initBeforesInfo.removeRedundantUpwards();
+
+ // if this images has initializer(s) (or +load), add it to list
+ MachOParser parser(_mh, _dyldCacheIsRaw);
+ Diagnostics diag;
+ if ( parser.hasInitializer(diag) || parser.hasPlusLoadMethod(diag) ) {
+ launch_cache::binary_format::ImageRef ref(0, _groupNum, _indexInGroup);
+ _initBeforesInfo.initBefore.push_back(ref);
+ }
+
+ //fprintf(stderr, "info for (%d, %d) %s\n", _group, _index, _runtimePath.c_str());
+ //for (ImageRef ref : _initBeforesInfo.initBefore)
+ // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.kind(), ref.group(), ref.index());
+ //for (ImageProxy* p : _initBeforesInfo.danglingUpward)
+ // fprintf(stderr, " up = %s\n", p->runtimePath().c_str());
+ }
+}
+
+void ImageProxy::convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup)
+{
+ if ( _initBeforesInfo.danglingUpward.empty() ) {
+ _initBeforesArray = _initBeforesInfo.initBefore;
+ }
+ else {
+ for (ImageRef ref : _initBeforesInfo.initBefore)
+ _initBeforesArray.push_back(ref);
+ bool inLibSys = inLibSystem();
+ for (ImageProxy* proxy : _initBeforesInfo.danglingUpward) {
+ // ignore upward dependendencies between stuff within libSystem.dylib
+ if ( inLibSys && proxy->inLibSystem() )
+ continue;
+ proxy->getInitBeforeList(owningGroup);
+ for (ImageRef depInit : proxy->_initBeforesInfo.initBefore) {
+ if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), depInit) == _initBeforesArray.end() )
+ _initBeforesArray.push_back(depInit);
+ }
+ ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup);
+ if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), ref) == _initBeforesArray.end() )
+ _initBeforesArray.push_back(ref);
+ }
+ }
+ //fprintf(stderr, "final init-before info for %s\n", _runtimePath.c_str());
+ //for (ImageRef ref : _initBeforesArray) {
+ // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.linkKind, ref.group, ref.index);
+ //}
+}
+
+const std::vector<ImageRef>& ImageProxy::getInitBeforeList(ImageProxyGroup& owningGroup)
+{
+ if ( !_initBeforesArraySet ) {
+ _initBeforesArraySet = true; // break cycles
+ recursiveBuildInitBeforeInfo(owningGroup);
+ convertInitBeforeInfoToArray(owningGroup);
+ }
+ return _initBeforesArray;
+}
+
+ImageProxy::FixupInfo ImageProxy::buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const
+{
+ __block ImageProxy::FixupInfo info;
+ MachOParser image(_mh, _dyldCacheIsRaw);
+
+ // add fixup for each rebase
+ __block bool rebaseError = false;
+ image.forEachRebase(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
+ dyld3::launch_cache::ImageGroupWriter::FixupType fixupType = launch_cache::ImageGroupWriter::FixupType::rebase;
+ switch ( type ) {
+ case REBASE_TYPE_POINTER:
+ fixupType = launch_cache::ImageGroupWriter::FixupType::rebase;
+ break;
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ fixupType = launch_cache::ImageGroupWriter::FixupType::rebaseText;
+ info.hasTextRelocs = true;
+ break;
+ case REBASE_TYPE_TEXT_PCREL32:
+ diag.error("pcrel text rebasing not supported");
+ stop = true;
+ rebaseError = true;
+ break;
+ default:
+ diag.error("unknown rebase type");
+ stop = true;
+ rebaseError = true;
+ break;
+ }
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeInvalid()});
+ //fprintf(stderr, "rebase: segIndex=%d, segOffset=0x%0llX, type=%d\n", segIndex, segOffset, type);
+ });
+ if ( diag.hasError() )
+ return FixupInfo();
+
+ // add fixup for each bind
+ image.forEachBind(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, int libOrdinal,
+ uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) {
+ launch_cache::ImageGroupWriter::FixupType fixupType;
+ switch ( type ) {
+ case BIND_TYPE_POINTER:
+ if ( lazy )
+ fixupType = launch_cache::ImageGroupWriter::FixupType::pointerLazyBind;
+ else
+ fixupType = launch_cache::ImageGroupWriter::FixupType::pointerBind;
+ break;
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ fixupType = launch_cache::ImageGroupWriter::FixupType::bindText;
+ info.hasTextRelocs = true;
+ break;
+ case BIND_TYPE_TEXT_PCREL32:
+ fixupType = launch_cache::ImageGroupWriter::FixupType::bindTextRel;
+ info.hasTextRelocs = true;
+ break;
+ case BIND_TYPE_IMPORT_JMP_REL32:
+ fixupType = launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel;
+ break;
+ default:
+ diag.error("malformed executable, unknown bind type (%d)", type);
+ stop = true;
+ return;
+ }
+ const ImageProxy* depProxy = nullptr;
+ bool isWeakDylib = false;
+ if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+ // -bundle_loader symbols cannot be bound ahead of time, we must look them up at load time
+ uint32_t imagePathPoolOffset = groupWriter.addString("@main");
+ uint32_t imageSymbolPoolOffset = groupWriter.addString(symbolName);
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)});
+ return;
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+ // -dynamic_lookup symbols cannot be bound ahead of time, we must look them up at load time
+ uint32_t imagePathPoolOffset = groupWriter.addString("@flat");
+ uint32_t imageSymbolPoolOffset = groupWriter.addString(symbolName);
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)});
+ return;
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+ depProxy = this;
+ }
+ else if ( (libOrdinal >= 1) && (libOrdinal <= _dependents.size()) ) {
+ isWeakDylib = (_dependentsKind[libOrdinal-1] == LinkKind::weak);
+ depProxy = _dependents[libOrdinal-1];
+ }
+ else {
+ diag.error("ordinal %d not supported", libOrdinal);
+ stop = true;
+ return;
+ }
+ if ( depProxy != nullptr ) {
+ MachOParser::FoundSymbol foundInfo;
+ if ( depProxy->findExportedSymbol(diag, symbolName, foundInfo) ) {
+ MachOParser implDylib(foundInfo.foundInDylib, _dyldCacheIsRaw);
+ switch ( foundInfo.kind ) {
+ case MachOParser::FoundSymbol::Kind::headerOffset:
+ case MachOParser::FoundSymbol::Kind::resolverOffset:
+ if ( implDylib.inDyldCache() ) {
+ uint32_t cacheOffset = (uint32_t)(implDylib.preferredLoadAddress() + foundInfo.value - cacheUnslideBaseAddress + addend);
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeSharedCacheOffset(cacheOffset)});
+ }
+ else {
+ ImageProxy* foundProxy = (ImageProxy*)(foundInfo.foundExtra);
+ bool isIndirectGroupNum = foundProxy->_groupNum >= 128;
+ uint32_t groupNum = isIndirectGroupNum ? groupWriter.addIndirectGroupNum(foundProxy->_groupNum) : foundProxy->_groupNum;
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeGroupValue(groupNum, foundProxy->_indexInGroup, foundInfo.value+addend, isIndirectGroupNum)});
+ }
+ break;
+ case MachOParser::FoundSymbol::Kind::absolute:
+ if (((((intptr_t)(foundInfo.value+addend)) << 2) >> 2) != (foundInfo.value+addend)) {
+ diag.error("absolute value %lld not supported", foundInfo.value+addend);
+ stop = true;
+ return;
+ }
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(foundInfo.value+addend)});
+ break;
+ }
+ }
+ else {
+ if ( !weakImport ) {
+ diag.error("symbol '%s' not found, expected in '%s'", symbolName, depProxy->runtimePath().c_str());
+ stop = true;
+ }
+ // mark fixup needs to set fixup location to zero
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
+ }
+ }
+ else {
+ if ( isWeakDylib ) {
+ // dylib not found and is weak, set pointers into it to zero
+ info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
+ }
+ else {
+ diag.error("dylib ordinal %d not found and not weak", libOrdinal);
+ stop = true;
+ }
+ }
+ });
+ if ( diag.hasError() )
+ return FixupInfo();
+
+ uint32_t weakDefPathPoolOffset = groupWriter.addString("@weak_def");
+ image.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) {
+ if ( strongDef )
+ return;
+ // find fixup for that location and change it to be a @weakdef dynamic target
+ bool altered = false;
+ for (FixUp& fixup : info.fixups) {
+ if ( (fixup.segOffset == segOffset) && (fixup.segIndex == segIndex) ) {
+ uint32_t symbolPoolOffset = groupWriter.addString(symbolName);
+ fixup.type = launch_cache::ImageGroupWriter::FixupType::pointerBind;
+ fixup.target = TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false);
+ altered = true;
+ }
+ }
+ if ( !altered ) {
+ if ( image.isSlideable() ) {
+ fprintf(stderr, "weak def for %s can't find underlying rebase/bind in %s\n", symbolName, runtimePath().c_str());
+ }
+ else {
+ // no-pie executable does not have rebase for weak-def fixup, so add fixup
+ uint32_t symbolPoolOffset = groupWriter.addString(symbolName);
+ info.fixups.push_back({segIndex, segOffset, launch_cache::ImageGroupWriter::FixupType::pointerBind, TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false)} );
+ }
+ }
+
+ });
+
+ return info;
+}
+
+
+void ImageProxy::setOverrideOf(uint32_t groupNum, uint32_t indexInGroup)
+{
+ _overrideOf = ImageRef(0, groupNum, indexInGroup);
+}
+
+
+static bool alreadyInList(const std::vector<ImageProxy*>& imageList, ImageProxy* image)
+{
+ for (ImageProxy* proxy : imageList) {
+ if ( proxy == image )
+ return true;
+ }
+ return false;
+}
+
+void ImageProxy::addToFlatLookup(std::vector<ImageProxy*>& imageList)
+{
+ // add all images shallow
+ bool addedSomething = false;
+ for (ImageProxy* dep : _dependents) {
+ if ( dep == nullptr )
+ continue;
+ if ( !alreadyInList(imageList, dep) ) {
+ imageList.push_back(dep);
+ addedSomething = true;
+ }
+ }
+ // recurse
+ if ( addedSomething ) {
+ for (ImageProxy* dep : _dependents) {
+ if ( dep == nullptr )
+ continue;
+ dep->addToFlatLookup(imageList);
+ }
+ }
+}
+
+
+/////////////////////////// ImageProxyGroup ///////////////////////////
+
+
+class StringPool
+{
+public:
+ uint32_t add(const std::string& str);
+ size_t size() const { return _buffer.size(); }
+ const char* buffer() const { return &_buffer[0]; }
+ void align();
+private:
+ std::vector<char> _buffer;
+ std::unordered_map<std::string, uint32_t> _existingEntries;
+};
+
+uint32_t StringPool::add(const std::string& str)
+{
+ auto pos = _existingEntries.find(str);
+ if ( pos != _existingEntries.end() )
+ return pos->second;
+ size_t len = str.size() + 1;
+ size_t offset = _buffer.size();
+ _buffer.insert(_buffer.end(), &str[0], &str[len]);
+ _existingEntries[str] = (uint32_t)offset;
+ assert(offset < 0xFFFF);
+ return (uint32_t)offset;
+}
+
+void StringPool::align()
+{
+ while ( (_buffer.size() % 4) != 0 )
+ _buffer.push_back('\0');
+}
+
+ImageProxyGroup::ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const launch_cache::binary_format::ImageGroup* basedOn,
+ ImageProxyGroup* next, const std::string& mainProgRuntimePath,
+ const std::vector<const BinaryImageGroupData*>& knownGroups,
+ const std::vector<std::string>& buildTimePrefixes,
+ const std::vector<std::string>& envVars,
+ bool stubsEliminated, bool dylibsExpectedOnDisk, bool inodesAreSameAsRuntime)
+ : _pathOverrides(envVars), _patchTable(nullptr), _basedOn(basedOn), _dyldCache(dyldCache), _nextSearchGroup(next), _groupNum(groupNum),
+ _stubEliminated(stubsEliminated), _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _inodesAreSameAsRuntime(inodesAreSameAsRuntime),
+ _knownGroups(knownGroups), _buildTimePrefixes(buildTimePrefixes), _mainProgRuntimePath(mainProgRuntimePath), _platform(Platform::unknown)
+{
+ _archName = dyldCache.cacheHeader()->archName();
+ _platform = (Platform)(dyldCache.cacheHeader()->platform());
+}
+
+
+ImageProxyGroup::~ImageProxyGroup()
+{
+ for (DyldSharedCache::MappedMachO& mapping : _ownedMappings ) {
+ vm_deallocate(mach_task_self(), (vm_address_t)mapping.mh, mapping.length);
+ }
+ for (ImageProxy* proxy : _images) {
+ delete proxy;
+ }
+}
+
+
+std::string ImageProxyGroup::normalizedPath(const std::string& path)
+{
+ for (const std::string& prefix : _buildTimePrefixes) {
+ std::string fullPath = prefix + path;
+ if ( fileExists(fullPath) ) {
+ if ( (fullPath.find("/../") != std::string::npos) || (fullPath.find("//") != std::string::npos) || (fullPath.find("/./") != std::string::npos) ) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) {
+ std::string resolvedUnPrefixed = &resolvedPath[prefix.size()];
+ return resolvedUnPrefixed;
+ }
+ }
+ break;
+ }
+ }
+ return path;
+}
+
+
+ImageProxy* ImageProxyGroup::findImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, ImageProxy::RPathChain* rChain)
+{
+ __block ImageProxy* result = nullptr;
+ _pathOverrides.forEachPathVariant(runtimeLoadPath.c_str(), _platform, ^(const char* possiblePath, bool& stop) {
+ if ( startsWith(possiblePath, "@rpath/") ) {
+ std::string trailing = &possiblePath[6];
+ for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) {
+ for (const std::string& rpath : cur->rpaths) {
+ std::string aPath = rpath + trailing;
+ result = findAbsoluteImage(diag, aPath, true, false);
+ if ( result != nullptr ) {
+ _pathToProxy[runtimeLoadPath] = result;
+ stop = true;
+ return;
+ }
+ }
+ }
+ // if cannot be found via current stack of rpaths, check if already found
+ auto pos = _pathToProxy.find(possiblePath);
+ if ( pos != _pathToProxy.end() ) {
+ result = pos->second;
+ stop = true;
+ return;
+ }
+ }
+ else if ( startsWith(possiblePath, "@loader_path/") ) {
+ std::string loaderFile = rChain->inProxy->runtimePath();
+ size_t lastSlash = loaderFile.rfind('/');
+ if ( lastSlash != std::string::npos ) {
+ std::string loaderDir = loaderFile.substr(0, lastSlash);
+ std::string newPath = loaderDir + &possiblePath[12];
+ result = findAbsoluteImage(diag, newPath, canBeMissing, false);
+ if ( result != nullptr ) {
+ _pathToProxy[runtimeLoadPath] = result;
+ stop = true;
+ return;
+ }
+ }
+ }
+ else if ( startsWith(possiblePath, "@executable_path/") ) {
+ for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) {
+ if ( cur->inProxy->mh()->filetype == MH_EXECUTE ) {
+ std::string mainProg = cur->inProxy->runtimePath();
+ size_t lastSlash = mainProg.rfind('/');
+ if ( lastSlash != std::string::npos ) {
+ std::string mainDir = mainProg.substr(0, lastSlash);
+ std::string newPath = mainDir + &possiblePath[16];
+ result = findAbsoluteImage(diag, newPath, canBeMissing, false);
+ if ( result != nullptr ) {
+ _pathToProxy[runtimeLoadPath] = result;
+ stop = true;
+ return;
+ }
+ }
+ }
+ }
+ }
+ else {
+ // load command is full path to dylib
+ result = findAbsoluteImage(diag, possiblePath, canBeMissing, false);
+ if ( result != nullptr ) {
+ stop = true;
+ return;
+ }
+ }
+ });
+
+ // when building closure, check if an added dylib is an override for something in the cache
+ if ( (result != nullptr) && (_groupNum > 1) && !result->isProxyForCachedDylib() ) {
+ for (ImageProxyGroup* grp = this; grp != nullptr; grp = grp->_nextSearchGroup) {
+ if ( grp->_basedOn == nullptr )
+ continue;
+ uint32_t indexInGroup;
+ launch_cache::ImageGroup imageGroup(grp->_basedOn);
+ if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), indexInGroup) ) {
+ result->setOverrideOf(imageGroup.groupNum(), indexInGroup);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+bool ImageProxyGroup::builtImageStillValid(const launch_cache::Image& image)
+{
+ // only do checks when running on system
+ if ( _buildTimePrefixes.size() != 1 )
+ return true;
+ if ( _buildTimePrefixes.front().size() != 0 )
+ return true;
+ if ( _platform != MachOParser::currentPlatform() )
+ return true;
+
+ struct stat statBuf;
+ bool expectedOnDisk = image.group().dylibsExpectedOnDisk();
+ bool overridableDylib = image.overridableDylib();
+ bool cachedDylib = !image.isDiskImage();
+ bool fileFound = ( ::stat(image.path(), &statBuf) == 0 );
+
+ if ( cachedDylib ) {
+ if ( expectedOnDisk ) {
+ if ( fileFound ) {
+ // macOS case: verify dylib file info matches what it was when cache was built
+ return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) );
+ }
+ else {
+ // macOS case: dylib missing
+ return false;
+ }
+ }
+ else {
+ if ( fileFound ) {
+ if ( overridableDylib ) {
+ // iOS case: internal install with dylib root
+ return false;
+ }
+ else {
+ // iOS case: customer install, ignore dylib on disk
+ return true;
+ }
+ }
+ else {
+ // iOS case: cached dylib not on disk as expected
+ return true;
+ }
+ }
+ }
+ else {
+ if ( fileFound ) {
+ if ( image.validateUsingModTimeAndInode() ) {
+ // macOS case: verify dylib file info matches what it was when cache was built
+ return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) );
+ }
+ else {
+ // FIXME: need to verify file cdhash
+ return true;
+ }
+ }
+ else {
+ // dylib not on disk as expected
+ return false;
+ }
+ }
+}
+
+ImageProxy* ImageProxyGroup::findAbsoluteImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, bool makeErrorMessage, bool pathIsAlreadyReal)
+{
+ auto pos = _pathToProxy.find(runtimeLoadPath);
+ if ( pos != _pathToProxy.end() )
+ return pos->second;
+
+ // see if this ImageProxyGroup is a proxy for an ImageGroup from the dyld shared cache
+ if ( _basedOn != nullptr ) {
+ uint32_t foundIndex;
+ launch_cache::ImageGroup imageGroup(_basedOn);
+ if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), foundIndex) ) {
+ launch_cache::Image image = imageGroup.image(foundIndex);
+ if ( builtImageStillValid(image) ) {
+ ImageProxy* proxy = nullptr;
+ if ( _groupNum == 0 ) {
+ const mach_header* mh = (mach_header*)((uint8_t*)(_dyldCache.cacheHeader()) + image.cacheOffset());
+ proxy = new ImageProxy(mh, image.binaryData(), foundIndex, _dyldCache.cacheIsMappedRaw());
+ }
+ else {
+ DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath);
+ if ( mapping != nullptr ) {
+ proxy = new ImageProxy(*mapping, _groupNum, foundIndex, false);
+ }
+ }
+ if ( proxy != nullptr ) {
+ _pathToProxy[runtimeLoadPath] = proxy;
+ _images.push_back(proxy);
+ if ( runtimeLoadPath != image.path() ) {
+ // lookup path is an alias, add real path too
+ _pathToProxy[image.path()] = proxy;
+ }
+ return proxy;
+ }
+ }
+ }
+ }
+
+ if ( _nextSearchGroup != nullptr ) {
+ ImageProxy* result = _nextSearchGroup->findAbsoluteImage(diag, runtimeLoadPath, true, false);
+ if ( result != nullptr )
+ return result;
+ }
+
+ // see if this is a symlink to a dylib
+ if ( !pathIsAlreadyReal ) {
+ for (const std::string& prefix : _buildTimePrefixes) {
+ std::string fullPath = prefix + runtimeLoadPath;
+ if ( endsWith(prefix, "/") )
+ fullPath = prefix.substr(0, prefix.size()-1) + runtimeLoadPath;
+ if ( fileExists(fullPath) ) {
+ std::string resolvedPath = realFilePath(fullPath);
+ if ( !resolvedPath.empty() && (resolvedPath!= fullPath) ) {
+ std::string resolvedRuntimePath = resolvedPath.substr(prefix.size());
+ ImageProxy* proxy = findAbsoluteImage(diag, resolvedRuntimePath, true, false, true);
+ if ( proxy != nullptr )
+ return proxy;
+ }
+ }
+ }
+ }
+
+ if ( (_groupNum >= 2) && (_basedOn == nullptr) ) {
+ if ( (runtimeLoadPath[0] != '/') && (runtimeLoadPath[0] != '@') ) {
+ for (ImageProxy* aProxy : _images) {
+ if ( endsWith(aProxy->runtimePath(), runtimeLoadPath) ) {
+ aProxy->setCwdMustBeThisDir();
+ return aProxy;
+ }
+ }
+ }
+
+ DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath);
+ if ( mapping != nullptr ) {
+ ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false);
+ _pathToProxy[runtimeLoadPath] = proxy;
+ _images.push_back(proxy);
+ return proxy;
+ }
+ }
+
+ if ( !canBeMissing && makeErrorMessage ) {
+ if ( diag.warnings().empty() ) {
+ if ( diag.hasError() ) {
+ std::string orgMsg = diag.errorMessage();
+ diag.error("'%s' %s", runtimeLoadPath.c_str(), orgMsg.c_str());
+ }
+ else {
+ diag.error("could not find '%s'", runtimeLoadPath.c_str());
+ }
+ }
+ else {
+ std::string allTries;
+ for (const std::string& warn : diag.warnings()) {
+ if ( allTries.empty() )
+ allTries = warn;
+ else
+ allTries = allTries + ", " + warn;
+ }
+ diag.clearWarnings();
+ diag.error("could not use '%s'. Did try: %s", runtimeLoadPath.c_str(), allTries.c_str());
+ }
+ }
+
+ // record locations not found so it can be verified they are still missing at runtime
+ _mustBeMissingFiles.insert(runtimeLoadPath);
+
+ return nullptr;
+}
+
+
+DyldSharedCache::MappedMachO* ImageProxyGroup::addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables)
+{
+ bool fileFound = false;
+ for (const std::string& prefix : _buildTimePrefixes) {
+ std::string fullPath = prefix + runtimePath;
+ struct stat statBuf;
+ if ( stat(fullPath.c_str(), &statBuf) != 0 )
+ continue;
+ fileFound = true;
+ // map whole file and determine if it is mach-o or a fat file
+ int fd = ::open(fullPath.c_str(), O_RDONLY);
+ if ( fd < 0 ) {
+ diag.warning("file not open()able '%s' errno=%d", fullPath.c_str(), errno);
+ continue;
+ }
+ size_t len = (size_t)statBuf.st_size;
+ size_t offset = 0;
+ const void* p = ::mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( p != MAP_FAILED ) {
+ size_t sliceLen;
+ size_t sliceOffset;
+ bool missingSlice;
+ Diagnostics fatDiag;
+ if ( FatUtil::isFatFileWithSlice(fatDiag, p, len, _archName, sliceOffset, sliceLen, missingSlice) ) {
+ // unmap whole file
+ ::munmap((void*)p, len);
+ // remap just slice
+ p = ::mmap(NULL, sliceLen, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+ if ( p != MAP_FAILED ) {
+ offset = sliceOffset;
+ len = sliceLen;
+ }
+ }
+ else if ( fatDiag.hasError() ) {
+ diag.warning("%s", fatDiag.errorMessage().c_str());
+ }
+ if ( (p != MAP_FAILED) && !missingSlice && MachOParser::isValidMachO(diag, _archName, _platform, p, len, fullPath, ignoreMainExecutables) ) {
+ bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ bool sip = false; // FIXME
+ _ownedMappings.emplace_back(runtimePath, (mach_header*)p, len, issetuid, sip, offset, statBuf.st_mtime, statBuf.st_ino);
+ ::close(fd);
+ return &_ownedMappings.back();
+ }
+ else if (p != MAP_FAILED) {
+ ::munmap((void*)p, len);
+ }
+ }
+ ::close(fd);
+ }
+ if ( !fileFound )
+ diag.warning("file not found '%s'", runtimePath.c_str());
+
+ return nullptr;
+}
+
+static bool dontExamineDir(const std::string& dirPath)
+{
+ return endsWith(dirPath, ".app") || endsWith(dirPath, ".xctoolchain") || endsWith(dirPath, ".sdk") || endsWith(dirPath, ".platform");
+}
+
+void ImageProxyGroup::addExtraMachOsInBundle(const std::string& appDir)
+{
+ iterateDirectoryTree("", appDir, ^(const std::string& dirPath) { return dontExamineDir(dirPath); }, ^(const std::string& path, const struct stat& statBuf) {
+ // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+ const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+ if ( !hasXBit )
+ return;
+
+ // ignore files too small
+ if ( statBuf.st_size < 0x1000 )
+ return;
+
+ // if the file is mach-o, add to list
+ if ( _pathToProxy.find(path) == _pathToProxy.end() ) {
+ Diagnostics machoDiag;
+ DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(machoDiag, path, true);
+ if ( mapping != nullptr ) {
+ ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false);
+ if ( proxy != nullptr ) {
+ _pathToProxy[path] = proxy;
+ _images.push_back(proxy);
+ }
+ }
+ }
+ });
+}
+
+// used when building dyld shared cache
+ImageProxyGroup* ImageProxyGroup::makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache,
+ const std::vector<DyldSharedCache::MappedMachO>& cachedDylibs,
+ const std::vector<std::string>& buildTimePrefixes,
+ const PatchTable& patchTable, bool stubEliminated, bool dylibsExpectedOnDisk)
+{
+ std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+ std::vector<const BinaryImageGroupData*> noExistingGroups;
+ ImageProxyGroup* groupProxy = new ImageProxyGroup(0, dyldCache, nullptr, nullptr, "", noExistingGroups, buildTimePrefixes, emptyEnvVars, stubEliminated, dylibsExpectedOnDisk);
+ groupProxy->_patchTable = &patchTable;
+
+ // add every dylib in shared cache to _images
+ uint32_t indexInGroup = 0;
+ for (const DyldSharedCache::MappedMachO& mapping : cachedDylibs) {
+ ImageProxy* proxy = new ImageProxy(mapping, 0, indexInGroup++, true);
+ groupProxy->_images.push_back(proxy);
+ groupProxy->_pathToProxy[mapping.runtimePath] = proxy;
+ }
+
+ // verify libdyld is compatible
+ ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef();
+ uint32_t libdyldEntryOffset;
+ groupProxy->findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset);
+ if ( diag.hasError() ) {
+ delete groupProxy;
+ return nullptr;
+ }
+
+ // wire up dependents
+ bool hadError = false;
+ for (size_t i=0; i < groupProxy->_images.size(); ++i) {
+ // note: addDependentsShallow() can append to _images, so can't use regular iterator
+ ImageProxy* proxy = groupProxy->_images[i];
+ proxy->addDependentsShallow(*groupProxy);
+ if ( proxy->diagnostics().hasError() ) {
+ hadError = true;
+ diag.copy(proxy->diagnostics());
+ break;
+ }
+ }
+
+ if ( hadError ) {
+ delete groupProxy;
+ return nullptr;
+ }
+
+ return groupProxy;
+}
+
+
+// used when building dyld shared cache
+ImageProxyGroup* ImageProxyGroup::makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+ const std::vector<DyldSharedCache::MappedMachO>& otherDylibsAndBundles,
+ bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes)
+{
+ std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+ const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+ std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData };
+ ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, buildTimePrefixes, emptyEnvVars);
+ ImageProxyGroup* groupProxy = new ImageProxyGroup(1, dyldCache, nullptr, cachedDylibsGroup, "", existingGroups, buildTimePrefixes, emptyEnvVars,
+ false, true, inodesAreSameAsRuntime);
+
+ // add every dylib/bundle in "other: list to _images
+ uint32_t indexInGroup = 0;
+ for (const DyldSharedCache::MappedMachO& mapping : otherDylibsAndBundles) {
+ ImageProxy* proxy = new ImageProxy(mapping, 1, indexInGroup++, false);
+ groupProxy->_images.push_back(proxy);
+ groupProxy->_pathToProxy[mapping.runtimePath] = proxy;
+ }
+
+ // wire up dependents
+ for (size_t i=0; i < groupProxy->_images.size(); ++i) {
+ // note: addDependentsShallow() can append to _images, so can't use regular iterator
+ ImageProxy* proxy = groupProxy->_images[i];
+ // note: other-dylibs can only depend on dylibs in this group or group 0, so no need for deep dependents
+ proxy->addDependentsShallow(*groupProxy);
+ if ( proxy->diagnostics().hasError() ) {
+ diag.warning("adding dependents to %s: %s", proxy->runtimePath().c_str(), proxy->diagnostics().errorMessage().c_str());
+ proxy->markInvalid();
+ }
+ }
+ // propagate invalidness
+ __block bool somethingInvalid;
+ do {
+ somethingInvalid = false;
+ for (ImageProxy* proxy : groupProxy->_images) {
+ proxy->forEachDependent(^(ImageProxy* dep, LinkKind) {
+ if ( (dep != nullptr) && dep->invalid() && !proxy->invalid()) {
+ proxy->markInvalid();
+ somethingInvalid = true;
+ }
+ });
+ }
+ } while (somethingInvalid);
+
+ return groupProxy;
+}
+
+// used by closured for dlopen of unknown dylibs
+const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum,
+ const std::vector<const BinaryImageGroupData*>& existingGroups,
+ const std::string& imagePath, const std::vector<std::string>& envVars)
+{
+ const std::vector<std::string>& noBuildTimePrefixes = {""};
+ ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, existingGroups[0], nullptr, "", existingGroups, noBuildTimePrefixes, envVars);
+ ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, nullptr, &dyldCacheDylibProxyGroup, "", existingGroups, noBuildTimePrefixes, envVars);
+ ImageProxyGroup dlopenGroupProxy(groupNum, dyldCache, nullptr, &dyldCacheOtherProxyGroup, imagePath, existingGroups, noBuildTimePrefixes, envVars, false, true, true);
+
+ DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, imagePath, true);
+ if ( topMapping == nullptr ) {
+ if ( diag.noError() ) {
+ const std::set<std::string>& warnings = diag.warnings();
+ if ( warnings.empty() )
+ diag.error("no loadable mach-o in %s", imagePath.c_str());
+ else
+ diag.error("%s", (*warnings.begin()).c_str());
+ }
+ return nullptr;
+ }
+
+ ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupNum, 0, false);
+ if ( topImageProxy == nullptr ) {
+ diag.error("can't find slice matching dyld cache in %s", imagePath.c_str());
+ return nullptr;
+ }
+ dlopenGroupProxy._images.push_back(topImageProxy);
+ dlopenGroupProxy._pathToProxy[imagePath] = topImageProxy;
+
+ // add all dylibs needed by dylib and are not in dyld cache
+ topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false);
+ if ( topImageProxy->diagnostics().hasError() ) {
+ diag.copy(topImageProxy->diagnostics());
+ return nullptr;
+ }
+
+ const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
+
+ return result;
+}
+
+
+// used when building dyld shared cache
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+ ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProgMapping,
+ bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes)
+{
+ // _basedOn can not be set until ImageGroup is built
+ if ( cachedDylibsGroup->_basedOn == nullptr ) {
+ cachedDylibsGroup->_basedOn = dyldCache.cachedDylibsGroup();
+ }
+ const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+ const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup();
+ std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+ std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+ ImageProxyGroup mainClosureGroupProxy(2, dyldCache, nullptr, otherOsDylibs, mainProgMapping.runtimePath, existingGroups, buildTimePrefixes,
+ emptyEnvVars, false, true, inodesAreSameAsRuntime);
+
+ ImageProxy* mainProxy = new ImageProxy(mainProgMapping, 2, 0, false);
+ if ( mainProxy == nullptr ) {
+ diag.error("can't find slice matching dyld cache in %s", mainProgMapping.runtimePath.c_str());
+ return nullptr;
+ }
+ mainClosureGroupProxy._images.push_back(mainProxy);
+ mainClosureGroupProxy._pathToProxy[mainProgMapping.runtimePath] = mainProxy;
+
+ return mainClosureGroupProxy.makeClosureBinary(diag, mainProxy, false);
+}
+
+
+bool ImageProxyGroup::addInsertedDylibs(Diagnostics& diag)
+{
+ __block bool success = true;
+ _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
+ ImageProxy* insertProxy = findAbsoluteImage(diag, dylibPath, false, true);
+ if ( insertProxy == nullptr )
+ success = false;
+ });
+ return success;
+}
+
+static DyldCacheParser findDyldCache(Diagnostics& diag, const ClosureBuffer::CacheIdent& cacheIdent, task_t requestor, bool* dealloc)
+{
+ *dealloc = false;
+#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+ size_t currentCacheSize;
+ const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(¤tCacheSize);
+ if ( currentCache != nullptr ) {
+ uuid_t currentCacheUUID;
+ currentCache->getUUID(currentCacheUUID);
+ if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) == 0 )
+ return DyldCacheParser((const DyldSharedCache*)currentCache, false);
+ }
+#endif
+ if ( requestor == mach_task_self() ) {
+ // handle dyld_closure_util case where -cache_file option maps raw cache file into this process
+ const DyldSharedCache* altCache = (DyldSharedCache*)cacheIdent.cacheAddress;
+ uuid_t altCacheUUID;
+ altCache->getUUID(altCacheUUID);
+ if ( memcmp(altCacheUUID, cacheIdent.cacheUUID, 16) == 0 )
+ return DyldCacheParser(altCache, true); // only one cache can be mapped into process, so this must be raw
+ else
+ diag.error("dyld cache uuid has changed");
+ }
+#if BUILDING_CLOSURED
+ else {
+ // handle case where requestor to closured is running with a different dyld cache that closured
+ uint8_t cacheBuffer[4096];
+ mach_vm_size_t actualReadSize = sizeof(cacheBuffer);
+ kern_return_t r;
+ r = mach_vm_read_overwrite(requestor, cacheIdent.cacheAddress, sizeof(cacheBuffer), (vm_address_t)&cacheBuffer, &actualReadSize);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("unable to read cache header from requesting process (addr=0x%llX), kern err=%d", cacheIdent.cacheAddress, r);
+ return DyldCacheParser(nullptr, false);
+ }
+ const dyld_cache_header* header = (dyld_cache_header*)cacheBuffer;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(cacheBuffer + header->mappingOffset);
+ vm_address_t bufferAddress = 0;
+ r = vm_allocate(mach_task_self(), &bufferAddress, (long)cacheIdent.cacheMappedSize, VM_FLAGS_ANYWHERE);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("unable to allocate space to copy custom dyld cache (size=0x%llX), kern err=%d", cacheIdent.cacheMappedSize, r);
+ return DyldCacheParser(nullptr, false);
+ }
+ uint64_t slide = cacheIdent.cacheAddress - mappings[0].address;
+ for (int i=0; i < 3; ++i) {
+ mach_vm_address_t mappedAddress = bufferAddress + (mappings[i].address - mappings[0].address);
+ mach_vm_size_t mappedSize = mappings[i].size;
+ vm_prot_t curProt = VM_PROT_READ;
+ vm_prot_t maxProt = VM_PROT_READ;
+ r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
+ requestor, mappings[i].address+slide, true, &curProt, &maxProt, VM_INHERIT_NONE);
+ if ( r != KERN_SUCCESS ) {
+ diag.error("unable to mach_vm_remap region %d custom dyld cache (request addr=0x%llX, size=0x%llX), kern err=%d, localBuffer=0x%lX, localMapTarget=0x%llX",
+ i, mappings[i].address+slide, mappedSize, r, (long)bufferAddress, mappedAddress);
+ return DyldCacheParser(nullptr, false);
+ }
+ if ( curProt != VM_PROT_READ )
+ vm_protect(mach_task_self(), (long)mappedAddress, (long)mappedSize, false, VM_PROT_READ);
+ }
+ *dealloc = true;
+ return DyldCacheParser((DyldSharedCache*)bufferAddress, false); // assumes cache in other process is mapped as three regions
+ }
+#endif
+ return DyldCacheParser(nullptr, false);
+}
+
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
+{
+ // unpack buffer
+ bool deallocCacheCopy;
+ DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
+ if ( diag.hasError() )
+ return nullptr;
+ const char* mainProg = buffer.targetPath();
+ std::vector<std::string> envVars;
+ int envCount = buffer.envVarCount();
+ const char* envVarCStrings[envCount];
+ buffer.copyImageGroups(envVarCStrings);
+ for (int i=0; i < envCount; ++i) {
+ envVars.push_back(envVarCStrings[i]);
+ }
+
+ // make ImageProxyGroups: 0, 1, 2
+ const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+ const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup();
+ std::vector<std::string> realBuildTimePrefixes;
+ for (const std::string& prefix : buildTimePrefixes) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(prefix.c_str(), resolvedPath) != nullptr )
+ realBuildTimePrefixes.push_back(resolvedPath);
+ else
+ realBuildTimePrefixes.push_back(prefix);
+ }
+ std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+ ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, realBuildTimePrefixes, envVars);
+ ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData, &dyldCacheDylibProxyGroup, "", existingGroups, realBuildTimePrefixes, envVars);
+ ImageProxyGroup mainClosureGroupProxy( 2, dyldCache, nullptr, &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true);
+
+ // add any DYLD_INSERTED_LIBRARIES then main program into closure
+ BinaryClosureData* result = nullptr;
+ if ( mainClosureGroupProxy.addInsertedDylibs(diag) ) {
+ ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true);
+ if ( proxy != nullptr ) {
+ // build closure
+ result = mainClosureGroupProxy.makeClosureBinary(diag, proxy, false);
+ }
+ }
+
+ // if client has a different cache, unmap our copy
+ if ( deallocCacheCopy )
+ vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+
+ return result;
+}
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
+{
+ Diagnostics diag;
+ const BinaryImageGroupData* newGroup = ImageProxyGroup::makeDlopenGroup(diag, input, mach_task_self(), {""});
+
+ if ( diag.noError() ) {
+ // on success return the ImageGroup binary in the ClosureBuffer
+ dyld3::ClosureBuffer result(newGroup);
+ free((void*)newGroup);
+ return result;
+ }
+ else {
+ // on failure return the error message in the ClosureBuffer
+ dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+ return err;
+ }
+}
+
+const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
+{
+ // unpack buffer
+ bool deallocCacheCopy;
+ DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
+ if ( diag.hasError() )
+ return nullptr;
+
+ const char* targetDylib = buffer.targetPath();
+ std::vector<std::string> envVars;
+ int envCount = buffer.envVarCount();
+ const char* envVarCStrings[envCount];
+ buffer.copyImageGroups(envVarCStrings);
+ for (int i=0; i < envCount; ++i) {
+ envVars.push_back(envVarCStrings[i]);
+ }
+ uint32_t groupCount = buffer.imageGroupCount() + 2;
+ const launch_cache::BinaryImageGroupData* groupDataPtrs[groupCount];
+ groupDataPtrs[0] = dyldCache.cachedDylibsGroup();
+ groupDataPtrs[1] = dyldCache.otherDylibsGroup();
+ buffer.copyImageGroups(&groupDataPtrs[2]);
+
+ // build an ImageProxyGroup for each existing group, and one for new group being constructed
+ std::vector<const launch_cache::BinaryImageGroupData*> existingGroups;
+ std::vector<std::unique_ptr<ImageProxyGroup>> proxies;
+ ImageProxyGroup* prevProxy = nullptr;
+ for (uint32_t i=0; i < groupCount; ++i) {
+ const launch_cache::BinaryImageGroupData* groupData = groupDataPtrs[i];
+ existingGroups.push_back(groupData);
+ launch_cache::ImageGroup group(groupData);
+ uint32_t groupNum = group.groupNum();
+ assert(groupNum == proxies.size());
+ proxies.emplace_back(new ImageProxyGroup(groupNum, dyldCache, groupData, prevProxy, "", existingGroups, buildTimePrefixes, envVars));
+ prevProxy = proxies.back().get();
+ }
+ ImageProxyGroup dlopenGroupProxy(groupCount, dyldCache, nullptr, prevProxy, targetDylib, existingGroups, buildTimePrefixes, envVars);
+
+ // find and mmap() top level dylib
+ DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, targetDylib, true);
+ if ( topMapping == nullptr ) {
+ std::string allWarnings;
+ for (const std::string& warn : diag.warnings()) {
+ if ( allWarnings.empty() )
+ allWarnings = warn;
+ else
+ allWarnings = allWarnings + ", " + warn;
+ }
+ diag.clearWarnings();
+ diag.error("%s", allWarnings.c_str());
+ if ( deallocCacheCopy )
+ vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+ return nullptr;
+ }
+
+ // make ImageProxy for top level dylib
+ ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupCount, 0, false);
+ if ( topImageProxy == nullptr ) {
+ diag.error("can't find slice matching dyld cache in %s", targetDylib);
+ if ( deallocCacheCopy )
+ vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+ return nullptr;
+ }
+ dlopenGroupProxy._images.push_back(topImageProxy);
+ dlopenGroupProxy._pathToProxy[targetDylib] = topImageProxy;
+
+ // add all dylibs needed by dylib and are not in dyld cache
+ topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false);
+ if ( topImageProxy->diagnostics().hasError() ) {
+ diag.copy(topImageProxy->diagnostics());
+ if ( deallocCacheCopy )
+ vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+ return nullptr;
+ }
+
+ // construct ImageGroup from ImageProxies
+ const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
+
+ // clean up
+ if ( deallocCacheCopy )
+ vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+
+ return result;
+}
+
+
+
+
+// Used by closured and dyld_closure_util
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache,
+ const std::string& mainProg, bool includeDylibsInDir,
+ const std::vector<std::string>& buildTimePrefixes,
+ const std::vector<std::string>& envVars)
+{
+ const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+ const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup();
+ std::vector<std::string> realBuildTimePrefixes;
+ for (const std::string& prefix : buildTimePrefixes) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(prefix.c_str(), resolvedPath) != nullptr )
+ realBuildTimePrefixes.push_back(resolvedPath);
+ else
+ realBuildTimePrefixes.push_back(prefix);
+ }
+ std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+ ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, realBuildTimePrefixes, envVars);
+ ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData, &dyldCacheDylibProxyGroup, "", existingGroups, realBuildTimePrefixes, envVars);
+ ImageProxyGroup mainClosureGroupProxy( 2, dyldCache, nullptr, &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true);
+
+ // add any DYLD_INSERTED_LIBRARIES into closure
+ if ( !mainClosureGroupProxy.addInsertedDylibs(diag) )
+ return nullptr;
+
+ ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true);
+ if ( proxy == nullptr )
+ return nullptr;
+
+ return mainClosureGroupProxy.makeClosureBinary(diag, proxy, includeDylibsInDir);
+}
+
+const char* sSkipPrograms_macOS[] = {
+ "/Applications/iBooks.app/Contents/MacOS/iBooks",
+};
+
+const char* sSkipPrograms_embeddedOSes[] = {
+ "/sbin/launchd",
+ "/usr/local/sbin/launchd.debug",
+ "/usr/local/sbin/launchd.development"
+};
+
+BinaryClosureData* ImageProxyGroup::makeClosureBinary(Diagnostics& diag, ImageProxy* mainProgProxy, bool includeDylibsInDir)
+{
+ assert(mainProgProxy != nullptr);
+ assert(_images.size() >= 1);
+
+ // check black list
+ if ( _platform == Platform::macOS ) {
+ for (const char* skipProg : sSkipPrograms_macOS) {
+ if ( mainProgProxy->runtimePath() == skipProg ) {
+ diag.error("black listed program");
+ return nullptr;
+ }
+ }
+ } else {
+ for (const char* skipProg : sSkipPrograms_embeddedOSes) {
+ if ( mainProgProxy->runtimePath() == skipProg ) {
+ diag.error("black listed program");
+ return nullptr;
+ }
+ }
+ }
+
+ _mainExecutableIndex = (uint32_t)_images.size() - 1;
+ // add all dylibs needed by main excutable and are not in dyld cache
+ mainProgProxy->addDependentsDeep(*this, nullptr, true);
+ if ( mainProgProxy->diagnostics().hasError() ) {
+ diag.copy(mainProgProxy->diagnostics());
+ return nullptr;
+ }
+
+ // if main program is in .app bundle, look for other mach-o files to add to closure for use by dlopen
+ bool isAppMainExecutable = false;
+ std::string appDir;
+ std::string leafName = basePath(mainProgProxy->runtimePath());
+ size_t posAppX = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".appex/");
+ size_t posApp = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".app/");
+ if ( posAppX != std::string::npos ) {
+ appDir = mainProgProxy->runtimePath().substr(0, posAppX+leafName.size()+7);
+ isAppMainExecutable = true;
+ }
+ else if ( posApp != std::string::npos ) {
+ appDir = mainProgProxy->runtimePath().substr(0, posApp+leafName.size()+5);
+ isAppMainExecutable = true;
+ }
+ if ( isAppMainExecutable ) {
+ addExtraMachOsInBundle(appDir);
+ for (size_t i=0; i < _images.size(); ++i) {
+ // note: addDependentsDeep() can append to _images, so can't use regular iterator
+ ImageProxy* aProxy = _images[i];
+ ImageProxy::RPathChain base = { aProxy, nullptr, mainProgProxy->rpaths() };
+ aProxy->addDependentsDeep(*this, &base, false);
+ if ( aProxy->diagnostics().hasError() ) {
+ aProxy->markInvalid();
+ diag.warning("%s could not be added to closure because %s", aProxy->runtimePath().c_str(), aProxy->diagnostics().errorMessage().c_str());
+ }
+ }
+ }
+ else if ( includeDylibsInDir ) {
+ size_t pos = mainProgProxy->runtimePath().rfind('/');
+ if ( pos != std::string::npos ) {
+ std::string mainDir = mainProgProxy->runtimePath().substr(0, pos);
+ addExtraMachOsInBundle(mainDir);
+ for (size_t i=0; i < _images.size(); ++i) {
+ // note: addDependentsDeep() can append to _images, so can't use regular iterator
+ ImageProxy* aProxy = _images[i];
+ aProxy->addDependentsDeep(*this, nullptr, false);
+ }
+ }
+ }
+
+ // add addition dependents of any inserted libraries
+ if ( _mainExecutableIndex != 0 ) {
+ for (uint32_t i=0; i < _mainExecutableIndex; ++i) {
+ _images[i]->addDependentsDeep(*this, nullptr, true);
+ if ( _images[i]->diagnostics().hasError() )
+ return nullptr;
+ }
+ }
+
+ // gather warnings from all statically dependent images
+ for (ImageProxy* proxy : _images) {
+ if ( !proxy->staticallyReferenced() && proxy->diagnostics().hasError() )
+ continue;
+ diag.copy(proxy->diagnostics());
+ if ( diag.hasError() ) {
+ return nullptr;
+ }
+ }
+
+ // get program entry
+ MachOParser mainExecutableParser(mainProgProxy->mh(), _dyldCache.cacheIsMappedRaw());
+ bool usesCRT;
+ uint32_t entryOffset;
+ mainExecutableParser.getEntry(entryOffset, usesCRT);
+
+ // build ImageGroupWriter
+ launch_cache::ImageGroupWriter groupWriter(_groupNum, mainExecutableParser.uses16KPages(), mainExecutableParser.is64(), _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
+ populateGroupWriter(diag, groupWriter);
+ if ( diag.hasError() )
+ return nullptr;
+
+ // pre-compute libSystem and libdyld into closure
+ ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef();
+ uint32_t libdyldEntryOffset;
+ findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset);
+ if ( diag.hasError() )
+ return nullptr;
+ ImageRef libSystemImageRef = ImageRef::makeEmptyImageRef();
+
+ findLibSystem(diag, mainExecutableParser.isSimulatorBinary(), libSystemImageRef);
+ if ( diag.hasError() )
+ return nullptr;
+
+ // build info about missing files and env vars
+ __block StringPool stringPool;
+ __block std::vector<uint32_t> envVarOffsets;
+ std::vector<uint16_t> missingFileComponentOffsets;
+ stringPool.add(" ");
+ for (const std::string& path : _mustBeMissingFiles) {
+ size_t start = 1;
+ size_t slashPos = path.find('/', start);
+ while (slashPos != std::string::npos) {
+ std::string component = path.substr(start, slashPos - start);
+ uint16_t offset = stringPool.add(component);
+ missingFileComponentOffsets.push_back(offset);
+ start = slashPos + 1;
+ slashPos = path.find('/', start);
+ }
+ std::string lastComponent = path.substr(start);
+ uint16_t offset = stringPool.add(lastComponent);
+ missingFileComponentOffsets.push_back(offset);
+ missingFileComponentOffsets.push_back(0); // mark end of a path
+ }
+ missingFileComponentOffsets.push_back(0); // mark end of all paths
+ if ( missingFileComponentOffsets.size() & 1 )
+ missingFileComponentOffsets.push_back(0); // 4-byte align array
+ __block uint32_t envVarCount = 0;
+ _pathOverrides.forEachEnvVar(^(const char* envVar) {
+ envVarOffsets.push_back(stringPool.add(envVar));
+ ++envVarCount;
+ });
+
+ // 4-byte align string pool size
+ stringPool.align();
+
+ // malloc a buffer and fill in ImageGroup part
+ uint32_t groupSize = groupWriter.size();
+ uint32_t missingFilesArraySize = (uint32_t)((missingFileComponentOffsets.size()*sizeof(uint16_t) + 3) & (-4));
+ uint32_t envVarsSize = (uint32_t)(envVarOffsets.size()*sizeof(uint32_t));
+ uint32_t stringPoolSize = (uint32_t)stringPool.size();
+ size_t allocSize = sizeof(launch_cache::binary_format::Closure)
+ + groupSize
+ + missingFilesArraySize
+ + envVarsSize
+ + stringPoolSize;
+ BinaryClosureData* clo = (BinaryClosureData*)malloc(allocSize);
+ groupWriter.finalizeTo(diag, _knownGroups, &clo->group);
+ launch_cache::ImageGroup cloGroup(&clo->group);
+ launch_cache::Image mainImage(cloGroup.imageBinary(_mainExecutableIndex));
+
+ uint32_t maxImageLoadCount = groupWriter.maxLoadCount(diag, _knownGroups, &clo->group);
+
+ if ( mainImage.isInvalid() ) {
+ free((void*)clo);
+ diag.error("depends on invalid dylib");
+ return nullptr;
+ }
+
+ // fill in closure attributes
+ clo->magic = launch_cache::binary_format::Closure::magicV1;
+ clo->usesCRT = usesCRT;
+ clo->isRestricted = mainProgProxy->isSetUID() || mainExecutableParser.isRestricted();
+ clo->usesLibraryValidation = mainExecutableParser.usesLibraryValidation();
+ clo->padding = 0;
+ clo->missingFileComponentsOffset = offsetof(launch_cache::binary_format::Closure, group) + groupSize;
+ clo->dyldEnvVarsOffset = clo->missingFileComponentsOffset + missingFilesArraySize;
+ clo->dyldEnvVarsCount = envVarCount;
+ clo->stringPoolOffset = clo->dyldEnvVarsOffset + envVarsSize;
+ clo->stringPoolSize = stringPoolSize;
+ clo->libSystemRef = libSystemImageRef;
+ clo->libDyldRef = libdyldEntryImageRef;
+ clo->libdyldVectorOffset = libdyldEntryOffset;
+ clo->mainExecutableIndexInGroup = _mainExecutableIndex;
+ clo->mainExecutableEntryOffset = entryOffset;
+ clo->initialImageCount = maxImageLoadCount;
+ _dyldCache.cacheHeader()->getUUID(clo->dyldCacheUUID);
+
+ if ( !mainExecutableParser.getCDHash(clo->mainExecutableCdHash) ) {
+ // if no code signature, fill in 16-bytes with UUID then 4 bytes of zero
+ bzero(clo->mainExecutableCdHash, 20);
+ mainExecutableParser.getUuid(clo->mainExecutableCdHash);
+ }
+ if ( missingFilesArraySize != 0 )
+ memcpy((uint8_t*)clo + clo->missingFileComponentsOffset, &missingFileComponentOffsets[0], missingFileComponentOffsets.size()*sizeof(uint16_t));
+ if ( envVarsSize != 0 )
+ memcpy((uint8_t*)clo + clo->dyldEnvVarsOffset, &envVarOffsets[0], envVarsSize);
+ if ( stringPool.size() != 0 )
+ memcpy((uint8_t*)clo + clo->stringPoolOffset, stringPool.buffer(), stringPool.size());
+
+ return clo;
+}
+
+const BinaryImageGroupData* ImageProxyGroup::makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[])
+{
+ const bool continueIfErrors = (_groupNum == 1);
+ bool uses16KPages = true;
+ bool is64 = true;
+ if ( !_images.empty() ) {
+ MachOParser firstParser(_images.front()->mh(), _dyldCache.cacheIsMappedRaw());
+ uses16KPages = firstParser.uses16KPages();
+ is64 = firstParser.is64();
+ }
+ launch_cache::ImageGroupWriter groupWriter(_groupNum, uses16KPages, is64, _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
+ populateGroupWriter(diag, groupWriter, neverEliminateStubs);
+ if ( diag.hasError() )
+ return nullptr;
+
+ // malloc a buffer and fill in ImageGroup part
+ BinaryImageGroupData* groupData = (BinaryImageGroupData*)malloc(groupWriter.size());
+ groupWriter.finalizeTo(diag, _knownGroups, groupData);
+
+ if ( !continueIfErrors && groupWriter.isInvalid(0) ) {
+ free((void*)groupData);
+ diag.error("depends on invalid dylib");
+ return nullptr;
+ }
+
+ return groupData;
+}
+
+
+void ImageProxyGroup::findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& vmOffsetInLibDyld)
+{
+ Diagnostics libDyldDiag;
+ ImageProxy* libDyldProxy = findImage(libDyldDiag, "/usr/lib/system/libdyld.dylib", false, nullptr);
+ if ( libDyldProxy == nullptr ) {
+ diag.error("can't find libdyld.dylib");
+ return;
+ }
+ ref = ImageRef(0, libDyldProxy->groupNum(), libDyldProxy->indexInGroup());
+
+ // find offset of "dyld3::entryVectorForDyld" in libdyld.dylib
+ Diagnostics entryDiag;
+ MachOParser::FoundSymbol dyldEntryInfo;
+ MachOParser libDyldParser(libDyldProxy->mh(), _dyldCache.cacheIsMappedRaw());
+ if ( !libDyldParser.findExportedSymbol(entryDiag, "__ZN5dyld318entryVectorForDyldE", nullptr, dyldEntryInfo, nullptr) ) {
+ diag.error("can't find dyld entry point into libdyld.dylib");
+ return;
+ }
+ vmOffsetInLibDyld = (uint32_t)dyldEntryInfo.value;
+ const LibDyldEntryVector* entry = (LibDyldEntryVector*)(libDyldParser.content(vmOffsetInLibDyld));
+ if ( entry == nullptr ) {
+ diag.error("dyld entry point at offset 0x%0X not found in libdyld.dylib", vmOffsetInLibDyld);
+ return;
+ }
+ if ( entry->vectorVersion != LibDyldEntryVector::kCurrentVectorVersion )
+ diag.error("libdyld.dylib vector version is incompatible with this dyld cache builder");
+ else if ( entry->binaryFormatVersion != launch_cache::binary_format::kFormatVersion )
+ diag.error("libdyld.dylib closures binary format version is incompatible with this dyld cache builder");
+}
+
+void ImageProxyGroup::findLibSystem(Diagnostics& diag, bool forSimulator, ImageRef& ref)
+{
+ Diagnostics libSysDiag;
+ ImageProxy* libSystemProxy = findImage(libSysDiag, forSimulator ? "/usr/lib/libSystem.dylib" : "/usr/lib/libSystem.B.dylib" , false, nullptr);
+ if ( libSystemProxy == nullptr ) {
+ diag.error("can't find libSystem.dylib");
+ return;
+ }
+ ref = ImageRef(0, libSystemProxy->groupNum(), libSystemProxy->indexInGroup());
+}
+
+
+std::vector<ImageProxy*> ImageProxyGroup::flatLookupOrder()
+{
+ std::vector<ImageProxy*> results;
+ // start with main executable and any inserted dylibs
+ for (uint32_t i=0; i <= _mainExecutableIndex; ++i)
+ results.push_back(_images[i]);
+
+ // recursive add dependents of main executable
+ _images[_mainExecutableIndex]->addToFlatLookup(results);
+
+ // recursive add dependents of any inserted dylibs
+ for (uint32_t i=0; i < _mainExecutableIndex; ++i)
+ _images[i]->addToFlatLookup(results);
+
+ return results;
+}
+
+void ImageProxyGroup::populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[])
+{
+ const bool buildingDylibsInCache = (_groupNum == 0);
+ const bool continueIfErrors = (_groupNum == 1);
+
+ std::unordered_set<std::string> neverStubEliminate;
+ if ( neverEliminateStubs != nullptr ) {
+ for (const char* const* nes=neverEliminateStubs; *nes != nullptr; ++nes)
+ neverStubEliminate.insert(*nes);
+ }
+
+ // pass 1: add all images
+ const uint64_t cacheUnslideBaseAddress = _dyldCache.cacheHeader()->unslidLoadAddress();
+ const uint32_t imageCount = (uint32_t)_images.size();
+ groupWriter.setImageCount(imageCount);
+ for (uint32_t i=0; i < imageCount; ++i) {
+ MachOParser imageParser(_images[i]->mh(), _dyldCache.cacheIsMappedRaw());
+ assert((imageParser.inDyldCache() == buildingDylibsInCache) && "all images must be same type");
+ // add info for each image
+ groupWriter.setImagePath(i, _images[i]->runtimePath().c_str());
+ groupWriter.setImageIsBundle(i, (imageParser.fileType() == MH_BUNDLE));
+ bool hasObjC = imageParser.hasObjC();
+ groupWriter.setImageHasObjC(i, hasObjC);
+ bool isEncrypted = imageParser.isEncrypted();
+ groupWriter.setImageIsEncrypted(i, isEncrypted);
+ bool mayHavePlusLoad = false;
+ if ( hasObjC ) {
+ mayHavePlusLoad = isEncrypted || imageParser.hasPlusLoadMethod(diag);
+ groupWriter.setImageMayHavePlusLoads(i, mayHavePlusLoad);
+ }
+ groupWriter.setImageHasWeakDefs(i, imageParser.hasWeakDefs());
+ groupWriter.setImageMustBeThisDir(i, _images[i]->cwdMustBeThisDir());
+ groupWriter.setImageIsPlatformBinary(i, _images[i]->isPlatformBinary());
+ groupWriter.setImageOverridableDylib(i, !_stubEliminated || (neverStubEliminate.count(_images[i]->runtimePath()) != 0));
+ uuid_t uuid;
+ if ( imageParser.getUuid(uuid) )
+ groupWriter.setImageUUID(i, uuid);
+ if ( _inodesAreSameAsRuntime ) {
+ groupWriter.setImageFileMtimeAndInode(i, _images[i]->fileModTime(), _images[i]->fileInode());
+ }
+ else {
+ uint8_t cdHash[20];
+ if ( !imageParser.getCDHash(cdHash) )
+ bzero(cdHash, 20);
+ // if image is not code signed, cdHash filled with all zeros
+ groupWriter.setImageCdHash(i, cdHash);
+ }
+ if ( !buildingDylibsInCache ) {
+ groupWriter.setImageSliceOffset(i, _images[i]->sliceFileOffset());
+ uint32_t fairPlayTextOffset;
+ uint32_t fairPlaySize;
+ if ( imageParser.isFairPlayEncrypted(fairPlayTextOffset, fairPlaySize) )
+ groupWriter.setImageFairPlayRange(i, fairPlayTextOffset, fairPlaySize);
+ uint32_t codeSigOffset;
+ uint32_t codeSigSize;
+ if ( imageParser.hasCodeSignature(codeSigOffset, codeSigSize) )
+ groupWriter.setImageCodeSignatureLocation(i, codeSigOffset, codeSigSize);
+ }
+ groupWriter.setImageDependentsCount(i, imageParser.dependentDylibCount());
+ // add segments to image
+ groupWriter.setImageSegments(i, imageParser, cacheUnslideBaseAddress);
+ // add initializers to image
+ __block std::vector<uint32_t> initOffsets;
+ imageParser.forEachInitializer(diag, ^(uint32_t offset) {
+ initOffsets.push_back(offset);
+ });
+ groupWriter.setImageInitializerOffsets(i, initOffsets);
+ if ( diag.hasError() && !continueIfErrors ) {
+ return;
+ }
+ // add DOFs to image
+ __block std::vector<uint32_t> dofOffsets;
+ imageParser.forEachDOFSection(diag, ^(uint32_t offset) {
+ dofOffsets.push_back(offset);
+ });
+ groupWriter.setImageDOFOffsets(i, dofOffsets);
+ if ( diag.hasError() && !continueIfErrors ) {
+ return;
+ }
+ bool neverUnload = false;
+ if ( buildingDylibsInCache )
+ neverUnload = true;
+ if ( _images[i]->staticallyReferenced() )
+ neverUnload = true;
+ if ( imageParser.hasObjC() && (imageParser.fileType() == MH_DYLIB) )
+ neverUnload = true;
+ if ( imageParser.hasThreadLocalVariables() )
+ neverUnload = true;
+ if ( !dofOffsets.empty() )
+ neverUnload = true;
+ groupWriter.setImageNeverUnload(i, neverUnload);
+ if ( _images[i]->invalid() )
+ groupWriter.setImageInvalid(i);
+ // record if this is an override of an OS dylib
+ ImageRef stdRef = _images[i]->overrideOf();
+ if ( stdRef != ImageRef::weakImportMissing() ) {
+ ImageRef thisImageRef(0, _groupNum, i);
+ groupWriter.addImageIsOverride(stdRef, thisImageRef);
+ }
+
+ // add alias if runtimepath does not match installName
+ if ( imageParser.fileType() == MH_DYLIB ) {
+ const char* installName = imageParser.installName();
+ if ( installName[0] == '/' ) {
+ if ( _images[i]->runtimePath() != installName ) {
+ // add install name as an alias
+ groupWriter.addImageAliasPath(i, installName);
+ }
+ }
+ // IOKit.framework on embedded uses not flat bundle, but clients dlopen() it as if it were flat
+ if ( buildingDylibsInCache && (_platform != Platform::macOS) && (_images[i]->runtimePath() == "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") ) {
+ groupWriter.addImageAliasPath(i, "/System/Library/Frameworks/IOKit.framework/IOKit");
+ }
+ }
+ }
+
+ // pass 2: add all dependencies (now that we have indexes defined)
+ for (uint32_t i=0; (i < imageCount) && diag.noError(); ++i) {
+ // add dependents to image
+ __block uint32_t depIndex = 0;
+ _images[i]->forEachDependent(^(ImageProxy* dep, LinkKind kind) {
+ if ( dep == nullptr ) {
+ if ( kind == LinkKind::weak )
+ groupWriter.setImageDependent(i, depIndex, launch_cache::binary_format::ImageRef::weakImportMissing());
+ else
+ groupWriter.setImageInvalid(i);
+ }
+ else {
+ launch_cache::binary_format::ImageRef ref((uint8_t)kind, dep->groupNum(), dep->indexInGroup());
+ groupWriter.setImageDependent(i, depIndex, ref);
+ }
+ ++depIndex;
+ });
+ }
+
+ // pass 3: invalidate any images dependent on invalid images)
+ if ( continueIfErrors ) {
+ const launch_cache::binary_format::ImageRef missingRef = launch_cache::binary_format::ImageRef::weakImportMissing();
+ __block bool somethingInvalidated = false;
+ do {
+ somethingInvalidated = false;
+ for (uint32_t i=0; i < imageCount; ++i) {
+ if ( groupWriter.isInvalid(i) )
+ continue;
+ uint32_t depCount = groupWriter.imageDependentsCount(i);
+ for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) {
+ launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex);
+ if ( ref == missingRef )
+ continue;
+ if ( ref.groupNum() == _groupNum ) {
+ if ( groupWriter.isInvalid(ref.indexInGroup()) ) {
+ // this image depends on something invalid, so mark it invalid
+ //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
+ groupWriter.setImageInvalid(i);
+ somethingInvalidated = true;
+ break;
+ }
+ }
+ }
+ }
+ } while (somethingInvalidated);
+ }
+
+ // pass 4: add fixups for each image, if needed
+ bool someBadFixups = false;
+ if ( !buildingDylibsInCache ) {
+ // compute fix ups for all images
+ __block std::vector<ImageProxy::FixupInfo> fixupInfos;
+ fixupInfos.resize(imageCount);
+ for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+ if ( groupWriter.isInvalid(imageIndex) )
+ continue;
+ Diagnostics fixupDiag;
+ fixupInfos[imageIndex] = _images[imageIndex]->buildFixups(fixupDiag, cacheUnslideBaseAddress, groupWriter);
+ if ( fixupDiag.hasError() ) {
+ // disable image in group
+ someBadFixups = true;
+ groupWriter.setImageInvalid(imageIndex);
+ if ( continueIfErrors ) {
+ diag.warning("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str());
+ continue;
+ }
+ else {
+ diag.error("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str());
+ return;
+ }
+ }
+ }
+ // if building closure, build patches to shared cache
+ if ( _groupNum == 2) {
+ std::unordered_set<ImageProxy*> staticImagesWithWeakDefs;
+ ImageProxyGroup* cacheGroup = _nextSearchGroup->_nextSearchGroup;
+ assert(cacheGroup->_basedOn != nullptr);
+ launch_cache::ImageGroup dyldCacheGroup(cacheGroup->_basedOn);
+ for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+ if ( groupWriter.isInvalid(imageIndex) )
+ continue;
+ ImageProxy* thisProxy = _images[imageIndex];
+ // Only process interposing info on dylibs statically linked into closure
+ if ( !thisProxy->staticallyReferenced() )
+ continue;
+ MachOParser imageParser(thisProxy->mh(), _dyldCache.cacheIsMappedRaw());
+ // if any images in closure interpose on something in dyld cache, record the cache patches needed
+ imageParser.forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& tupleStop) {
+ if ( _groupNum != 2 ) {
+ groupWriter.setImageInvalid(imageIndex);
+ return;
+ }
+ TargetSymbolValue interposeReplacee = TargetSymbolValue::makeInvalid();
+ TargetSymbolValue interposeReplacement = TargetSymbolValue::makeInvalid();
+ for (const FixUp& fixup : fixupInfos[imageIndex].fixups) {
+ if ( fixup.segIndex != segIndex )
+ continue;
+ if ( fixup.segOffset == replacementSegOffset ) {
+ if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::rebase ) {
+ uint64_t offsetInImage = replacementContent - imageParser.preferredLoadAddress();
+ interposeReplacement = TargetSymbolValue::makeGroupValue(2, imageIndex, offsetInImage, false);
+ }
+ else {
+ diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
+ return;
+ }
+ }
+ else if ( fixup.segOffset == replaceeSegOffset ) {
+ if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::pointerBind ) {
+ interposeReplacee = fixup.target;
+ }
+ else {
+ diag.warning("bad interposing target in %s", _images[imageIndex]->runtimePath().c_str());
+ return;
+ }
+ }
+ }
+ // scan through fixups of other images in closure looking to see what functions this entry references
+ for (uint32_t otherIndex=0; otherIndex < imageCount; ++otherIndex) {
+ if ( otherIndex == imageIndex )
+ continue;
+ for (FixUp& fixup : fixupInfos[otherIndex].fixups) {
+ switch ( fixup.type ) {
+ case launch_cache::ImageGroupWriter::FixupType::pointerBind:
+ case launch_cache::ImageGroupWriter::FixupType::pointerLazyBind:
+ // alter fixup to use interposed function instead of requested
+ if ( fixup.target == interposeReplacee )
+ fixup.target = interposeReplacement;
+ break;
+ case launch_cache::ImageGroupWriter::FixupType::rebase:
+ case launch_cache::ImageGroupWriter::FixupType::rebaseText:
+ case launch_cache::ImageGroupWriter::FixupType::ignore:
+ case launch_cache::ImageGroupWriter::FixupType::bindText:
+ case launch_cache::ImageGroupWriter::FixupType::bindTextRel:
+ case launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel:
+ break;
+ }
+ }
+ }
+ if ( interposeReplacee.isInvalid() || interposeReplacement.isInvalid() ) {
+ diag.error("malformed interposing section in %s", _images[imageIndex]->runtimePath().c_str());
+ tupleStop = true;
+ return;
+ }
+ // record any overrides in shared cache that will need to be applied at launch time
+ uint64_t offsetInCache;
+ if ( interposeReplacee.isSharedCacheTarget(offsetInCache) ) {
+ uint32_t patchTableIndex;
+ if ( dyldCacheGroup.hasPatchTableIndex((uint32_t)offsetInCache, patchTableIndex) ) {
+ uint32_t replacementGroupNum;
+ uint32_t replacementIndexInGroup;
+ uint64_t replacementOffsetInImage;
+ assert(interposeReplacement.isGroupImageTarget(replacementGroupNum, replacementIndexInGroup, replacementOffsetInImage));
+ assert(replacementGroupNum == 2);
+ assert(replacementIndexInGroup < (1 << 8));
+ assert(replacementOffsetInImage < 0xFFFFFFFFULL);
+ DyldCacheOverride cacheOverride;
+ cacheOverride.patchTableIndex = patchTableIndex;
+ cacheOverride.imageIndex = replacementIndexInGroup;
+ cacheOverride.imageOffset = replacementOffsetInImage;
+ _cacheOverrides.push_back(cacheOverride);
+ }
+ }
+ });
+ if ( diag.hasError() && !continueIfErrors ) {
+ return;
+ }
+ // if any dylibs in the closure override a dyld cache dylib, then record the cache patches needed
+ ImageRef overrideOf = thisProxy->overrideOf();
+ if ( (overrideOf != ImageRef::makeEmptyImageRef()) && (overrideOf.groupNum() == 0) ) {
+ //fprintf(stderr, "need to patch %s into cache\n", thisProxy->runtimePath().c_str());
+ const launch_cache::Image imageInCache = dyldCacheGroup.image(overrideOf.indexInGroup());
+ const mach_header* imageInCacheMH = (mach_header*)((char*)(_dyldCache.cacheHeader()) + imageInCache.cacheOffset());
+ MachOParser inCacheParser(imageInCacheMH, _dyldCache.cacheIsMappedRaw());
+ // walk all exported symbols in dylib in cache
+ inCacheParser.forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, bool isReExport, bool &stop) {
+ if ( isReExport )
+ return;
+ uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + imageOffset);
+ //fprintf(stderr, " patch cache offset 0x%08X which is %s\n", cacheOffsetOfSymbol, symbolName);
+ // for each exported symbol, see if it is in patch table (used by something else in cache)
+ uint32_t patchTableIndex;
+ if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
+ //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
+ // lookup address of symbol in override dylib and add patch info
+ MachOParser::FoundSymbol foundInfo;
+ if ( imageParser.findExportedSymbol(diag, symbolName, nullptr, foundInfo, nullptr) ) {
+ DyldCacheOverride cacheOverride;
+ assert(patchTableIndex < (1 << 24));
+ assert(thisProxy->indexInGroup() < (1 << 8));
+ assert(foundInfo.value < (1ULL << 32));
+ cacheOverride.patchTableIndex = patchTableIndex;
+ cacheOverride.imageIndex = thisProxy->indexInGroup();
+ cacheOverride.imageOffset = foundInfo.value;
+ _cacheOverrides.push_back(cacheOverride);
+ }
+ }
+ });
+ }
+ // save off all images in closure with weak defines
+ if ( thisProxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK) ) {
+ staticImagesWithWeakDefs.insert(thisProxy);
+ }
+ }
+ // if any dylibs in the closure override a weak symbol in a cached dylib, then record the cache patches needed
+ if ( !staticImagesWithWeakDefs.empty() ) {
+ // build list of all weak def symbol names
+ __block std::unordered_map<std::string, DyldCacheOverride> weakSymbols;
+ for (ImageProxy* proxy : staticImagesWithWeakDefs ) {
+ MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+ weakDefParser.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) {
+ weakSymbols[symbolName] = { 0, 0, 0 };
+ });
+ }
+ // do a flat namespace walk of all images
+ std::vector<ImageProxy*> flatSearchOrder = flatLookupOrder();
+ for (ImageProxy* proxy : flatSearchOrder) {
+ // only look at images that participate in weak coalescing
+ if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
+ continue;
+ // look only at images in closure
+ if ( proxy->groupNum() == 2 ) {
+ MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+ // check if this closure image defines any of the not-yet found weak symbols
+ for (auto& entry : weakSymbols ) {
+ if ( entry.second.imageOffset != 0 )
+ continue;
+ Diagnostics weakDiag;
+ MachOParser::FoundSymbol foundInfo;
+ if ( weakDefParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
+ assert(proxy->indexInGroup() < (1 << 8));
+ assert(foundInfo.value < (1ULL << 32));
+ entry.second.imageIndex = proxy->indexInGroup();
+ entry.second.imageOffset = foundInfo.value;
+ }
+ }
+ }
+ }
+ for (ImageProxy* proxy : flatSearchOrder) {
+ // only look at images that participate in weak coalescing
+ if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
+ continue;
+ // look only at images in dyld cache
+ if ( proxy->groupNum() == 0 ) {
+ const launch_cache::Image imageInCache = dyldCacheGroup.image(proxy->indexInGroup());
+ MachOParser inCacheParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+ Diagnostics cacheDiag;
+ for (auto& entry : weakSymbols) {
+ if ( entry.second.imageOffset == 0 )
+ continue;
+ Diagnostics weakDiag;
+ MachOParser::FoundSymbol foundInfo;
+ if ( inCacheParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
+ uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + foundInfo.value);
+ // see if this symbol is in patch table (used by something else in cache)
+ uint32_t patchTableIndex;
+ if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
+ //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
+ DyldCacheOverride cacheOverride;
+ cacheOverride.patchTableIndex = patchTableIndex;
+ cacheOverride.imageIndex = entry.second.imageIndex;
+ cacheOverride.imageOffset = entry.second.imageOffset;
+ _cacheOverrides.push_back(cacheOverride);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // record fixups for each image
+ for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+ groupWriter.setImageFixups(diag, imageIndex, fixupInfos[imageIndex].fixups, fixupInfos[imageIndex].hasTextRelocs);
+ }
+ }
+
+ // pass 5: invalidate any images dependent on invalid images)
+ if ( someBadFixups && continueIfErrors ) {
+ __block bool somethingInvalidated = false;
+ do {
+ somethingInvalidated = false;
+ for (uint32_t i=0; i < imageCount; ++i) {
+ if ( groupWriter.isInvalid(i) )
+ continue;
+ uint32_t depCount = groupWriter.imageDependentsCount(i);
+ for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) {
+ launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex);
+ if ( ref.groupNum() == _groupNum ) {
+ if ( groupWriter.isInvalid(ref.indexInGroup()) ) {
+ // this image depends on something invalid, so mark it invalid
+ //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
+ groupWriter.setImageInvalid(i);
+ somethingInvalidated = true;
+ break;
+ }
+ }
+ }
+ }
+ } while (somethingInvalidated);
+ }
+
+ // pass 6: compute initializer lists for each image
+ const bool log = false;
+ for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+ if ( groupWriter.isInvalid(imageIndex) )
+ continue;
+
+ auto inits = _images[imageIndex]->getInitBeforeList(*this);
+ if ( log && buildingDylibsInCache ) {
+ fprintf(stderr, "%s\n init list: ", _images[imageIndex]->runtimePath().c_str());
+ for (launch_cache::binary_format::ImageRef ref : inits) {
+ if ( ref.groupNum() == 0 ) {
+ std::string dep = _images[ref.indexInGroup()]->runtimePath();
+ size_t off = dep.rfind('/');
+ fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+ groupWriter.setImageInitBefore(imageIndex, inits);
+ }
+
+ // pass 7: compute DOFs
+ for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+ if ( groupWriter.isInvalid(imageIndex) )
+ continue;
+
+ auto inits = _images[imageIndex]->getInitBeforeList(*this);
+ if ( log && buildingDylibsInCache ) {
+ fprintf(stderr, "%s\n DOFs: ", _images[imageIndex]->runtimePath().c_str());
+ for (launch_cache::binary_format::ImageRef ref : inits) {
+ if ( ref.groupNum() == 0 ) {
+ std::string dep = _images[ref.indexInGroup()]->runtimePath();
+ size_t off = dep.rfind('/');
+ fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+ groupWriter.setImageInitBefore(imageIndex, inits);
+ }
+
+ // pass 8: add patch table entries iff this is dyld cache ImageGroup
+ assert(buildingDylibsInCache == (_patchTable != nullptr));
+ if ( _patchTable != nullptr ) {
+ for (uint32_t i=0; i < imageCount; ++i) {
+ const auto pos = _patchTable->find(_images[i]->mh());
+ if ( pos != _patchTable->end() ) {
+ for (const auto& entry : pos->second ) {
+ uint32_t defFunctionOffset = entry.first;
+ groupWriter.setImagePatchLocations(i, defFunctionOffset, entry.second);
+ }
+ }
+ }
+ }
+
+ // if this is a main closure group with an interposing dylib, add cache overrides
+ if ( !_cacheOverrides.empty() ) {
+ groupWriter.setGroupCacheOverrides(_cacheOverrides);
+ }
+
+ // align string pool
+ groupWriter.alignStringPool();
+}
+
+
+
+} // namespace dyld3
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 ImageProxy_h
+#define ImageProxy_h
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <set>
+#include <unordered_map>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "LaunchCache.h"
+#include "LaunchCacheWriter.h"
+#include "PathOverrides.h"
+#include "ClosureBuffer.h"
+#include "DyldCacheParser.h"
+
+
+namespace dyld3 {
+
+typedef launch_cache::binary_format::Image BinaryImageData;
+typedef launch_cache::binary_format::ImageGroup BinaryImageGroupData;
+typedef launch_cache::binary_format::Closure BinaryClosureData;
+typedef launch_cache::binary_format::ImageRef ImageRef;
+typedef launch_cache::Image::LinkKind LinkKind;
+typedef launch_cache::ImageGroupWriter::FixUp FixUp;
+typedef launch_cache::binary_format::DyldCacheOverride DyldCacheOverride;
+typedef launch_cache::ImageGroupList ImageGroupList;
+
+
+
+
+class ImageProxyGroup;
+
+class ImageProxy
+{
+public:
+ ImageProxy(const mach_header* mh, const BinaryImageData* image, uint32_t indexInGroup, bool dyldCacheIsRaw);
+ ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw);
+
+ struct RPathChain {
+ ImageProxy* inProxy;
+ const RPathChain* prev;
+ const std::vector<std::string>& rpaths;
+ };
+
+ struct InitOrderInfo {
+ bool beforeHas(ImageRef);
+ bool upwardHas(ImageProxy*);
+ void removeRedundantUpwards();
+ std::vector<ImageRef> initBefore;
+ std::vector<ImageProxy*> danglingUpward;
+ };
+
+ struct FixupInfo {
+ std::vector<FixUp> fixups;
+ bool hasTextRelocs = false;
+ };
+
+ void recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup);
+ void addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* chain=nullptr);
+ void addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* chain, bool staticallyReferenced);
+ void markInvalid() { _invalid = true; }
+
+ uint32_t groupNum() const { return _groupNum; }
+ uint32_t indexInGroup() const { return _indexInGroup; }
+ const mach_header* mh() const { return _mh; }
+ const std::string& runtimePath() const { return _runtimePath; }
+ uint64_t sliceFileOffset() const { return _sliceFileOffset; }
+ uint64_t fileModTime() const { return _modTime; }
+ uint64_t fileInode() const { return _inode; }
+ bool isSetUID() const { return _isSetUID; }
+ bool invalid() const { return _invalid; }
+ bool staticallyReferenced() const { return _staticallyReferenced; }
+ bool cwdMustBeThisDir() const { return _cwdMustBeThisDir; }
+ bool isPlatformBinary() const { return _platformBinary; }
+ bool isProxyForCachedDylib() const { return _imageBinaryData != nullptr; }
+ const Diagnostics& diagnostics() const { return _diag; }
+ ImageRef overrideOf() const { return _overrideOf; }
+ bool inLibSystem() const;
+ void setCwdMustBeThisDir() { _cwdMustBeThisDir = true; }
+ void setPlatformBinary() { _platformBinary = true; }
+ void setOverrideOf(uint32_t groupNum, uint32_t indexInGroup);
+ void checkIfImageOverride(const std::string& runtimeLoadPath);
+ void forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const;
+ FixupInfo buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const;
+ bool findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const;
+ void convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup);
+ void addToFlatLookup(std::vector<ImageProxy*>& imageList);
+ const std::vector<ImageRef>& getInitBeforeList(ImageProxyGroup& owningGroup);
+ const std::vector<std::string>& rpaths() { return _rpaths; }
+
+private:
+ void processRPaths(ImageProxyGroup& owningGroup);
+
+ const mach_header* const _mh;
+ uint64_t const _sliceFileOffset;
+ uint64_t const _modTime;
+ uint64_t const _inode;
+ const BinaryImageData* const _imageBinaryData; // only used if proxy is for image in shared cache
+ std::string const _runtimePath;
+ bool const _isSetUID;
+ bool const _dyldCacheIsRaw;
+ uint32_t const _groupNum;
+ uint32_t const _indexInGroup;
+ bool _platformBinary;
+ Diagnostics _diag;
+ std::vector<ImageProxy*> _dependents;
+ std::vector<LinkKind> _dependentsKind;
+ std::vector<std::string> _rpaths;
+ InitOrderInfo _initBeforesInfo;
+ std::vector<ImageRef> _initBeforesArray;
+ ImageRef _overrideOf;
+ bool _directDependentsSet;
+ bool _deepDependentsSet;
+ bool _initBeforesArraySet;
+ bool _initBeforesComputed;
+ bool _invalid;
+ bool _staticallyReferenced;
+ bool _cwdMustBeThisDir;
+};
+
+
+class ImageProxyGroup
+{
+public:
+ ~ImageProxyGroup();
+
+
+ typedef std::unordered_map<const mach_header*, std::unordered_map<uint32_t, std::unordered_set<uint32_t>>> PatchTable;
+
+
+ // used when building dyld shared cache
+ static ImageProxyGroup* makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, const std::vector<DyldSharedCache::MappedMachO>& cachedDylibs,
+ const std::vector<std::string>& buildTimePrefixes, const PatchTable& patchTable,
+ bool stubEliminated, bool dylibsExpectedOnDisk);
+
+ // used when building dyld shared cache
+ static ImageProxyGroup* makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+ const std::vector<DyldSharedCache::MappedMachO>& otherDylibsAndBundles,
+ bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes);
+
+ const BinaryImageGroupData* makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[]=nullptr);
+
+ // used when building dyld shared cache
+ static BinaryClosureData* makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+ ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProg,
+ bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes);
+
+ // used by closured for dlopen of unknown dylibs
+ static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum,
+ const std::vector<const BinaryImageGroupData*>& existingGroups,
+ const std::string& imagePath, const std::vector<std::string>& envVars);
+
+ static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes={});
+
+ static BinaryClosureData* makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes={});
+
+
+ //
+ // Creates a binary launch closure for the specified main executable.
+ // Used by closured and dyld_closure_util
+ //
+ // The closure is allocated with malloc(). Use free() to release when done.
+ // The size of the closure can be determined using Closure::size().
+ // If the closure cannot be built (e.g. app needs a symbol not exported by a framework),
+ // the reason for the failure is returned as a string in the diag parameter.
+ // The mainProgRuntimePath path is the path the program will be at runtime.
+ // The buildTimePrefixes is a list of prefixes to add to each path during closure
+ // creation to find the files at buildtime.
+ //
+ static BinaryClosureData* makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache,
+ const std::string& mainProgRuntimePath, bool includeDylibsInDir,
+ const std::vector<std::string>& buildTimePrefixes={},
+ const std::vector<std::string>& envVars={});
+
+
+private:
+ friend class ImageProxy;
+
+ ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const BinaryImageGroupData* basedOn,
+ ImageProxyGroup* next, const std::string& mainProgRuntimePath,
+ const std::vector<const BinaryImageGroupData*>& knownGroups,
+ const std::vector<std::string>& buildTimePrefixes,
+ const std::vector<std::string>& envVars,
+ bool stubsEliminated=false, bool dylibsExpectedOnDisk=true, bool inodesAreSameAsRuntime=true);
+
+ ImageProxy* findImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, ImageProxy::RPathChain*);
+ ImageProxy* findAbsoluteImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, bool makeErrorMessage, bool pathIsReal=false);
+ bool builtImageStillValid(const launch_cache::Image& image);
+ const std::string& mainProgRuntimePath() { return _mainProgRuntimePath; }
+ DyldSharedCache::MappedMachO* addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables=false);
+ BinaryClosureData* makeClosureBinary(Diagnostics& diag, ImageProxy* mainProg, bool includeDylibsInDir);
+ void findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& offset);
+ void findLibSystem(Diagnostics& diag, bool sim, ImageRef& ref);
+ void populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[]=nullptr);
+ std::string normalizedPath(const std::string& path);
+ void addExtraMachOsInBundle(const std::string& appDir);
+ bool addInsertedDylibs(Diagnostics& diag);
+ std::vector<ImageProxy*> flatLookupOrder();
+
+ PathOverrides _pathOverrides;
+ const BinaryImageGroupData* _basedOn; // if not null, then lazily populate _images
+ const PatchTable* _patchTable;
+ ImageProxyGroup* const _nextSearchGroup;
+ const DyldCacheParser _dyldCache;
+ uint32_t const _groupNum;
+ bool const _stubEliminated;
+ bool const _dylibsExpectedOnDisk;
+ bool const _inodesAreSameAsRuntime;
+ uint32_t _mainExecutableIndex;
+ std::vector<const BinaryImageGroupData*> _knownGroups;
+ std::vector<ImageProxy*> _images;
+ std::unordered_map<std::string, ImageProxy*> _pathToProxy;
+ std::vector<DyldSharedCache::MappedMachO> _ownedMappings;
+ std::vector<std::string> _buildTimePrefixes;
+ std::vector<DyldCacheOverride> _cacheOverrides;
+ std::string _mainProgRuntimePath;
+ std::string _archName;
+ Platform _platform;
+ std::set<std::string> _mustBeMissingFiles;
+};
+
+
+
+
+
+}
+
+#endif // ImageProxy_h
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+*/
+#ifndef __MACH_O_FILE_ABSTRACTION__
+#define __MACH_O_FILE_ABSTRACTION__
+
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+#include <mach/machine.h>
+
+// suport older versions of mach-o/loader.h
+#ifndef LC_UUID
+#define LC_UUID 0x1b
+struct uuid_command {
+ uint32_t cmd; /* LC_UUID */
+ uint32_t cmdsize; /* sizeof(struct uuid_command) */
+ uint8_t uuid[16]; /* the 128-bit uuid */
+};
+#endif
+
+#ifndef S_16BYTE_LITERALS
+ #define S_16BYTE_LITERALS 0xE
+#endif
+
+#ifndef CPU_SUBTYPE_ARM_V5TEJ
+ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
+#endif
+#ifndef CPU_SUBTYPE_ARM_XSCALE
+ #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7
+ #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7F
+ #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7K
+ #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7S
+ #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11)
+#endif
+#ifndef CPU_SUBTYPE_ARM64_ALL
+ #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0)
+#endif
+#ifndef CPU_TYPE_ARM64
+ #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64))
+#endif
+
+#define ARM64_RELOC_UNSIGNED 0 // for pointers
+
+
+#ifndef LC_LOAD_UPWARD_DYLIB
+ #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
+#endif
+
+#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
+ #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
+#endif
+#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT
+ #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
+#endif
+
+#ifndef LC_FUNCTION_STARTS
+ #define LC_FUNCTION_STARTS 0x26
+#endif
+
+#ifndef LC_DATA_IN_CODE
+ #define LC_DATA_IN_CODE 0x29
+#endif
+
+#ifndef LC_DYLIB_CODE_SIGN_DRS
+ #define LC_DYLIB_CODE_SIGN_DRS 0x2B
+#endif
+
+#ifndef CPU_SUBTYPE_X86_64_H
+ #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8)
+#endif
+
+
+#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
+
+#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01
+#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02
+#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03
+#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04
+#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05
+#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06
+#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07
+#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08
+#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09
+#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A
+#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B
+#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C
+
+#define MH_HAS_OBJC 0x40000000
+
+#include "FileAbstraction.hpp"
+//#include "Architectures.hpp"
+
+// utility to pair together a cpu-type and cpu-sub-type
+struct ArchPair
+{
+ uint32_t arch;
+ uint32_t subtype;
+
+ ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {}
+
+ bool operator<(const ArchPair& other) const {
+ if ( this->arch != other.arch )
+ 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;
+ }
+};
+
+
+//
+// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness
+//
+
+//
+// mach-o load command
+//
+template <typename P>
+class macho_load_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(command.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); }
+
+ typedef typename P::E E;
+private:
+ load_command command;
+};
+
+
+//
+// mach-o segment load command
+//
+template <typename P> struct macho_segment_content {};
+template <> struct macho_segment_content<Pointer32<BigEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
+template <> struct macho_segment_content<Pointer64<BigEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
+template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
+template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
+
+template <typename P>
+class macho_segment_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); }
+
+ const char* segname() const INLINE { return segment.fields.segname; }
+ void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); }
+
+ uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); }
+ void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); }
+
+ uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); }
+ void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); }
+
+ uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); }
+ void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); }
+
+ uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); }
+ void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); }
+
+ uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); }
+ void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); }
+
+ uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); }
+ void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); }
+
+ uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); }
+ void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); }
+
+ uint32_t flags() const INLINE { return E::get32(segment.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); }
+
+ enum {
+ CMD = macho_segment_content<P>::CMD
+ };
+
+ typedef typename P::E E;
+private:
+ macho_segment_content<P> segment;
+};
+
+
+//
+// mach-o section
+//
+template <typename P> struct macho_section_content {};
+template <> struct macho_section_content<Pointer32<BigEndian> > { section fields; };
+template <> struct macho_section_content<Pointer64<BigEndian> > { section_64 fields; };
+template <> struct macho_section_content<Pointer32<LittleEndian> > { section fields; };
+template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64 fields; };
+
+template <typename P>
+class macho_section {
+public:
+ const char* sectname() const INLINE { return section.fields.sectname; }
+ void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); }
+
+ const char* segname() const INLINE { return section.fields.segname; }
+ void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); }
+
+ uint64_t addr() const INLINE { return P::getP(section.fields.addr); }
+ void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); }
+
+ uint64_t size() const INLINE { return P::getP(section.fields.size); }
+ void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); }
+
+ uint32_t offset() const INLINE { return E::get32(section.fields.offset); }
+ void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); }
+
+ uint32_t align() const INLINE { return E::get32(section.fields.align); }
+ void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); }
+
+ uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); }
+ void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); }
+
+ uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); }
+ void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); }
+
+ uint32_t flags() const INLINE { return E::get32(section.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); }
+
+ uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); }
+ void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); }
+
+ uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); }
+ void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); }
+
+ typedef typename P::E E;
+private:
+ macho_section_content<P> section;
+};
+
+
+//
+// mach-o dylib load command
+//
+template <typename P>
+class macho_dylib_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); }
+ void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); }
+
+ uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); }
+ void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); }
+
+ uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); }
+ void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); }
+
+ uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); }
+ void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); }
+
+ const char* name() const INLINE { return (const char*)&fields + name_offset(); }
+ void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ dylib_command fields;
+};
+
+
+//
+// mach-o dylinker load command
+//
+template <typename P>
+class macho_dylinker_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); }
+ void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); }
+
+ const char* name() const INLINE { return (const char*)&fields + name_offset(); }
+ void set_name_offset() INLINE { set_name_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ dylinker_command fields;
+};
+
+
+//
+// mach-o sub_framework load command
+//
+template <typename P>
+class macho_sub_framework_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); }
+ void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); }
+
+ const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); }
+ void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_framework_command fields;
+};
+
+
+//
+// mach-o sub_client load command
+//
+template <typename P>
+class macho_sub_client_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); }
+ void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); }
+
+ const char* client() const INLINE { return (const char*)&fields + client_offset(); }
+ void set_client_offset() INLINE { set_client_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_client_command fields;
+};
+
+
+//
+// mach-o sub_umbrella load command
+//
+template <typename P>
+class macho_sub_umbrella_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); }
+ void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); }
+
+ const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); }
+ void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_umbrella_command fields;
+};
+
+
+//
+// mach-o sub_library load command
+//
+template <typename P>
+class macho_sub_library_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); }
+ void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); }
+
+ const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); }
+ void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); }
+
+ typedef typename P::E E;
+private:
+ sub_library_command fields;
+};
+
+
+//
+// mach-o uuid load command
+//
+template <typename P>
+class macho_uuid_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ const uint8_t* uuid() const INLINE { return fields.uuid; }
+ void set_uuid(uint8_t value[16]) INLINE { memcpy(&fields.uuid, value, 16); }
+
+ typedef typename P::E E;
+private:
+ uuid_command fields;
+};
+
+
+//
+// mach-o routines load command
+//
+template <typename P> struct macho_routines_content {};
+template <> struct macho_routines_content<Pointer32<BigEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
+template <> struct macho_routines_content<Pointer64<BigEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
+template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
+template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
+
+template <typename P>
+class macho_routines_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); }
+
+ uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); }
+ void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); }
+
+ uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); }
+ void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); }
+
+ uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); }
+ void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); }
+
+ uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); }
+ void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); }
+
+ uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); }
+ void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); }
+
+ uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); }
+ void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); }
+
+ uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); }
+ void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); }
+
+ uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); }
+ void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); }
+
+ typedef typename P::E E;
+ enum {
+ CMD = macho_routines_content<P>::CMD
+ };
+private:
+ macho_routines_content<P> routines;
+};
+
+
+//
+// mach-o symbol table load command
+//
+template <typename P>
+class macho_symtab_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t symoff() const INLINE { return E::get32(fields.symoff); }
+ void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); }
+
+ uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); }
+ void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); }
+
+ uint32_t stroff() const INLINE { return E::get32(fields.stroff); }
+ void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); }
+
+ uint32_t strsize() const INLINE { return E::get32(fields.strsize); }
+ void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); }
+
+
+ typedef typename P::E E;
+private:
+ symtab_command fields;
+};
+
+
+//
+// mach-o dynamic symbol table load command
+//
+template <typename P>
+class macho_dysymtab_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); }
+ void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); }
+
+ uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); }
+ void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); }
+
+ uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); }
+ void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); }
+
+ uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); }
+ void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); }
+
+ uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); }
+ void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); }
+
+ uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); }
+ void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); }
+
+ uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); }
+ void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); }
+
+ uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); }
+ void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); }
+
+ uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); }
+ void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); }
+
+ uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); }
+ void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); }
+
+ uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); }
+ void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); }
+
+ uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); }
+ void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); }
+
+ uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); }
+ void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); }
+
+ uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); }
+ void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); }
+
+ uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); }
+ void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); }
+
+ uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); }
+ void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); }
+
+ uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); }
+ void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); }
+
+ uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); }
+ void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); }
+
+ typedef typename P::E E;
+private:
+ dysymtab_command fields;
+};
+
+
+//
+// mach-o two-level hints load command
+//
+template <typename P>
+class macho_twolevel_hints_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t offset() const INLINE { return E::get32(fields.offset); }
+ void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); }
+
+ uint32_t nhints() const INLINE { return E::get32(fields.nhints); }
+ void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); }
+
+ typedef typename P::E E;
+private:
+ twolevel_hints_command fields;
+};
+
+
+//
+// mach-o threads load command
+//
+template <typename P>
+class macho_thread_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t flavor() const INLINE { return E::get32(fields_flavor); }
+ void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); }
+
+ uint32_t count() const INLINE { return E::get32(fields_count); }
+ void set_count(uint32_t value) INLINE { E::set32(fields_count, value); }
+
+ uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); }
+ void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); }
+
+ typedef typename P::E E;
+ typedef typename P::uint_t pint_t;
+private:
+ struct thread_command fields;
+ uint32_t fields_flavor;
+ uint32_t fields_count;
+ pint_t thread_registers[1];
+};
+
+
+//
+// mach-o misc data
+//
+template <typename P>
+class macho_linkedit_data_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); }
+ void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); }
+
+ uint32_t datasize() const INLINE { return E::get32(fields.datasize); }
+ void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); }
+
+
+ typedef typename P::E E;
+private:
+ linkedit_data_command fields;
+};
+
+
+//
+// mach-o symbol table entry
+//
+template <typename P> struct macho_nlist_content {};
+template <> struct macho_nlist_content<Pointer32<BigEndian> > { struct nlist fields; };
+template <> struct macho_nlist_content<Pointer64<BigEndian> > { struct nlist_64 fields; };
+template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist fields; };
+template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64 fields; };
+
+template <typename P>
+class macho_nlist {
+public:
+ uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); }
+ void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); }
+
+ uint8_t n_type() const INLINE { return entry.fields.n_type; }
+ void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; }
+
+ uint8_t n_sect() const INLINE { return entry.fields.n_sect; }
+ void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; }
+
+ uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); }
+ void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); }
+
+ uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); }
+ void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); }
+
+ typedef typename P::E E;
+private:
+ macho_nlist_content<P> entry;
+};
+
+
+
+//
+// mach-o relocation info
+//
+template <typename P>
+class macho_relocation_info {
+public:
+ uint32_t r_address() const INLINE { return E::get32(address); }
+ void set_r_address(uint32_t value) INLINE { E::set32(address, value); }
+
+ uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); }
+ void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); }
+
+ bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); }
+ void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); }
+
+ uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); }
+ void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); }
+
+ bool r_extern() const INLINE { return E::getBits(other, 27, 1); }
+ void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); }
+
+ uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); }
+ void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); }
+
+ void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
+
+ typedef typename P::E E;
+private:
+ uint32_t address;
+ uint32_t other;
+};
+
+
+//
+// mach-o scattered relocation info
+// The bit fields are always in big-endian order (see mach-o/reloc.h)
+//
+template <typename P>
+class macho_scattered_relocation_info {
+public:
+ bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); }
+ void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); }
+
+ bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); }
+ void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); }
+
+ uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); }
+ void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); }
+
+ uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); }
+ void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); }
+
+ uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); }
+ void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); }
+
+ uint32_t r_value() const INLINE { return E::get32(value); }
+ void set_r_value(uint32_t x) INLINE { E::set32(value, x); }
+
+ uint32_t r_other() const INLINE { return other; }
+
+ typedef typename P::E E;
+private:
+ uint32_t other;
+ uint32_t value;
+};
+
+
+//
+// mach-o file header
+//
+template <typename P> struct macho_header_content {};
+template <> struct macho_header_content<Pointer32<BigEndian> > { mach_header fields; };
+template <> struct macho_header_content<Pointer64<BigEndian> > { mach_header_64 fields; };
+template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header fields; };
+template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64 fields; };
+
+template <typename P>
+class macho_header {
+public:
+ uint32_t magic() const INLINE { return E::get32(header.fields.magic); }
+ void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); }
+
+ uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); }
+ void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); }
+
+ uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); }
+ void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); }
+
+ uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); }
+ void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); }
+
+ uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); }
+ void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); }
+
+ uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); }
+ void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); }
+
+ uint32_t flags() const INLINE { return E::get32(header.fields.flags); }
+ void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); }
+
+ uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); }
+ void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); }
+
+ const macho_segment_command<P>* getSegment(const char *segname) const
+ {
+ const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
+ uint32_t cmd_count = this->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;
+ if (0 == strncmp(segname, segcmd->segname(), 16)) {
+ return segcmd;
+ }
+ }
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return NULL;
+ }
+
+ const macho_section<P>* getSection(const char *segname, const char *sectname) const
+ {
+ const macho_segment_command<P>* segcmd = getSegment(segname);
+ if (!segcmd) return NULL;
+
+ const macho_section<P>* sectcmd = (macho_section<P>*)(segcmd+1);
+ uint32_t section_count = segcmd->nsects();
+ for (uint32_t j = 0; j < section_count; ++j) {
+ if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) {
+ return sectcmd+j;
+ }
+ }
+
+ if (strcmp(segname, "__DATA") == 0)
+ return getSection("__DATA_CONST", sectname);
+ return NULL;
+ }
+
+ const macho_load_command<P>* getLoadCommand(int query) const
+ {
+ const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
+ uint32_t cmd_count = this->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == query ) {
+ return cmd;
+ }
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return NULL;
+ }
+
+ typedef typename P::E E;
+private:
+ macho_header_content<P> header;
+};
+
+
+
+//
+// compressed dyld info load command
+//
+template <typename P>
+class macho_dyld_info_command {
+public:
+ uint32_t cmd() const INLINE { return E::get32(fields.cmd); }
+ void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); }
+
+ uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); }
+ void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); }
+
+ uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); }
+ void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); }
+
+ uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); }
+ void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); }
+
+ uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); }
+ void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); }
+
+ uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); }
+ void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); }
+
+ uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); }
+ void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); }
+
+ uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); }
+ void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); }
+
+ uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); }
+ void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); }
+
+ uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); }
+ void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); }
+
+ uint32_t export_off() const INLINE { return E::get32(fields.export_off); }
+ void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); }
+
+ uint32_t export_size() const INLINE { return E::get32(fields.export_size); }
+ void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); }
+
+
+ typedef typename P::E E;
+private:
+ dyld_info_command fields;
+};
+
+#ifndef NO_ULEB
+inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) {
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ if (p == end)
+ throw "malformed uleb128 extends beyond trie";
+
+ uint64_t slice = *p & 0x7f;
+
+ if (bit >= 64 || slice << bit >> bit != slice)
+ throw "uleb128 too big for 64-bits";
+ else {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ }
+ while (*p++ & 0x80);
+ return result;
+}
+
+
+inline int64_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;
+}
+
+#endif
+
+
+#endif // __MACH_O_FILE_ABSTRACTION__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 Manifest_h
+#define Manifest_h
+
+#include <map>
+#include <set>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <assert.h>
+#include <uuid/uuid.h>
+
+#import <Foundation/Foundation.h>
+
+#include "MachOParser.h"
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+
+extern std::string toolDir();
+
+namespace dyld3 {
+
+struct BuildQueueEntry {
+ DyldSharedCache::CreateOptions options;
+ std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
+ std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
+ std::vector<DyldSharedCache::MappedMachO> mainExecutables;
+ std::string outputPath;
+ std::set<std::string> configNames;
+};
+
+struct Manifest {
+ struct UUIDInfo {
+ const mach_header* mh;
+ uint64_t sliceFileOffset;
+ std::size_t size;
+ std::string runtimePath;
+ std::string buildPath;
+ std::string installName;
+ std::string arch;
+ UUID uuid;
+ UUIDInfo(const mach_header* M, std::size_t S, uint64_t SO, UUID U, std::string A, std::string RP, std::string BP, std::string IN)
+ : mh(M), size(S), arch(A), uuid(U), runtimePath(RP), buildPath(BP), installName(IN), sliceFileOffset(SO) {}
+ UUIDInfo() : UUIDInfo(nullptr, 0, 0, UUID(), "", "", "", "") {}
+ };
+
+ struct Project {
+ std::vector<std::string> sources;
+ };
+
+ struct File {
+ MachOParser* parser;
+ File(MachOParser* P)
+ : parser(P)
+ {
+ }
+ };
+
+ struct SegmentInfo {
+ std::string name;
+ uint64_t startAddr;
+ uint64_t endAddr;
+ };
+
+ struct CacheInfo {
+ std::vector<SegmentInfo> regions;
+ std::string cdHash;
+ };
+
+ struct CacheImageInfo {
+ bool included;
+ std::string exclusionInfo;
+ UUID uuid;
+ std::string installname;
+ std::vector<SegmentInfo> segments;
+ CacheImageInfo(void)
+ : included(true)
+ {
+ }
+ };
+
+ struct Results {
+ std::string failure;
+ std::map<UUID, CacheImageInfo> dylibs;
+ std::map<UUID, CacheImageInfo> bundles;
+ std::map<UUID, CacheImageInfo> executables;
+
+ std::set<std::string> warnings;
+ CacheInfo developmentCache;
+ CacheInfo productionCache;
+ CacheImageInfo& dylibForInstallname(const std::string& installname);
+ void exclude(MachOParser* parser, const std::string& reason);
+ void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
+ };
+
+ struct Architecture {
+ mutable Results results;
+
+ bool operator==(const Architecture& O) const;
+ bool operator!=(const Architecture& other) const;
+ };
+
+ struct Configuration {
+ std::string platformName;
+ std::string device;
+ std::string disposition;
+ std::string metabomTag;
+ std::set<std::string> metabomTags;
+ std::set<std::string> metabomExcludeTags;
+ std::set<std::string> metabomRestrictTags;
+ std::set<std::string> restrictedInstallnames;
+ std::map<std::string, Architecture> architectures;
+
+ bool operator==(const Configuration& O) const;
+ bool operator!=(const Configuration& other) const;
+ const Architecture& architecture(const std::string& architecture) const;
+ void forEachArchitecture(std::function<void(const std::string& archName)> lambda) const;
+ };
+
+ const std::map<std::string, Project>& projects();
+ const Configuration& configuration(const std::string& configuration) const;
+ void forEachConfiguration(std::function<void(const std::string& configName)> lambda) const;
+
+ void addProjectSource(const std::string& project, const std::string& source, bool first = false);
+
+ const std::string projectPath(const std::string& projectName);
+ const bool empty(void);
+ const std::string dylibOrderFile() const;
+ void setDylibOrderFile(const std::string& dylibOrderFile);
+
+ const std::string dirtyDataOrderFile() const;
+ void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile);
+
+ const std::string metabomFile() const;
+ void setMetabomFile(const std::string& metabomFile);
+
+ const Platform platform() const;
+ void setPlatform(const Platform platform);
+
+ const std::string& build() const;
+ void setBuild(const std::string& build);
+ const uint32_t version() const;
+ void setVersion(const uint32_t manifestVersion);
+ bool normalized;
+
+ Manifest(Diagnostics& D, const std::string& path);
+ Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays);
+
+ BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose);
+
+ void write(const std::string& path);
+ void writeJSON(const std::string& path);
+ void canonicalize(void);
+ void calculateClosure();
+ MachOParser parserForUUID(const UUID& uuid) const;
+ const std::string buildPathForUUID(const UUID& uuid);
+ const std::string runtimePathForUUID(const UUID& uuid);
+ DyldSharedCache::MappedMachO machoForPathAndArch(const std::string& path, const std::string& arch) const;
+ void remove(const std::string& config, const std::string& arch);
+ const std::string removeLargestLeafDylib(const std::set<std::string>& configurations, const std::string& architecture);
+ void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
+ bool filterForConfig(const std::string& configName);
+
+private:
+ NSDictionary* _manifestDict;
+ Diagnostics& _diags;
+ std::map<UUID, UUIDInfo> _uuidMap;
+ std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
+ static dispatch_queue_t _identifierQueue;
+ uint32_t _manifestVersion;
+ std::string _build;
+ std::string _dylibOrderFile;
+ std::string _dirtyDataOrderFile;
+ std::string _metabomFile;
+ Platform _platform;
+ std::map<std::string, Project> _projects;
+ std::map<std::string, Configuration> _configurations;
+ std::map<std::string, std::set<std::string>> _metabomTagMap;
+ std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
+ std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
+
+ std::vector<DyldSharedCache::MappedMachO> dylibsForCache(const std::string& configuration, const std::string& architecture);
+ std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles(const std::string& configuration, const std::string& architecture);
+ std::vector<DyldSharedCache::MappedMachO> mainExecutables(const std::string& configuration, const std::string& architecture);
+
+ const UUIDInfo& infoForUUID(const UUID& uuid) const;
+ const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const;
+ void insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo);
+ bool loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures);
+ bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set<std::string>& architectures);
+ void removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration, const std::string& architecture,
+ std::unordered_set<UUID>& processedIdentifiers);
+ void dedupeDispositions();
+ void calculateClosure(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
+/*
+ * Copyright (c) 2017 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@
+ */
+
+
+extern "C" {
+#include <Bom/Bom.h>
+#include <Metabom/MBTypes.h>
+#include <Metabom/MBEntry.h>
+#include <Metabom/MBMetabom.h>
+#include <Metabom/MBIterator.h>
+};
+
+#include <algorithm>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+#include <Foundation/Foundation.h>
+
+#include "MachOFileAbstraction.hpp"
+#include "FileAbstraction.hpp"
+#include "Trie.hpp"
+#include "FileUtils.h"
+#include "StringUtils.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+#include <array>
+#include <vector>
+
+#include "Manifest.h"
+
+namespace {
+//FIXME this should be in a class
+static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
+
+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;
+}
+
+//hACK: If we declare this in manifest
+static NSDictionary* gManifestDict;
+
+} /* Anonymous namespace */
+
+namespace dyld3 {
+void Manifest::Results::exclude(MachOParser* parser, const std::string& reason)
+{
+ auto dylibUUID = parser->uuid();
+ dylibs[dylibUUID].uuid = dylibUUID;
+ dylibs[dylibUUID].installname = parser->installName();
+ dylibs[dylibUUID].included = false;
+ dylibs[dylibUUID].exclusionInfo = reason;
+}
+
+void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason)
+{
+ auto parser = manifest.parserForUUID(uuid);
+ dylibs[uuid].uuid = uuid;
+ dylibs[uuid].installname = parser.installName();
+ dylibs[uuid].included = false;
+ dylibs[uuid].exclusionInfo = reason;
+}
+
+Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname)
+{
+ auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<UUID, CacheImageInfo> d) { return d.second.installname == installname; });
+ assert(i != dylibs.end());
+ return i->second;
+}
+
+bool Manifest::Architecture::operator==(const Architecture& O) const
+{
+ 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
+ || Odylib->second.uuid != dylib.second.uuid)
+ return false;
+ }
+ }
+
+ for (const 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
+ || dylib->second.uuid != Odylib.second.uuid)
+ return false;
+ }
+ }
+
+ for (auto& bundle : results.bundles) {
+ if (bundle.second.included) {
+ auto Obundle = O.results.bundles.find(bundle.first);
+ if (Obundle == O.results.bundles.end()
+ || Obundle->second.included == false
+ || Obundle->second.uuid != bundle.second.uuid)
+ return false;
+ }
+ }
+
+ for (const auto& Obundle : O.results.bundles) {
+ if (Obundle.second.included) {
+ auto bundle = results.bundles.find(Obundle.first);
+ if (bundle == results.bundles.end()
+ || bundle->second.included == false
+ || bundle->second.uuid != Obundle.second.uuid)
+ return false;
+ }
+ }
+
+ for (auto& executable : results.executables) {
+ if (executable.second.included) {
+ auto Oexecutable = O.results.executables.find(executable.first);
+ if (Oexecutable == O.results.executables.end()
+ || Oexecutable->second.included == false
+ || Oexecutable->second.uuid != executable.second.uuid)
+ return false;
+ }
+ }
+
+ for (const auto& Oexecutable : O.results.executables) {
+ if (Oexecutable.second.included) {
+ auto executable = results.executables.find(Oexecutable.first);
+ if (executable == results.executables.end()
+ || executable->second.included == false
+ || executable->second.uuid != Oexecutable.second.uuid)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Manifest::Configuration::operator==(const Configuration& O) const
+{
+ return architectures == O.architectures;
+}
+
+bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); }
+
+const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const
+{
+ assert(architectures.find(architecture) != architectures.end());
+ return architectures.find(architecture)->second;
+}
+
+void Manifest::Configuration::forEachArchitecture(std::function<void(const std::string& archName)> lambda) const
+{
+ for (const auto& architecutre : architectures) {
+ lambda(architecutre.first);
+ }
+}
+
+bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); }
+
+const std::map<std::string, Manifest::Project>& Manifest::projects()
+{
+ return _projects;
+}
+
+const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const
+{
+ assert(_configurations.find(configuration) != _configurations.end());
+ return _configurations.find(configuration)->second;
+}
+
+void Manifest::forEachConfiguration(std::function<void(const std::string& configName)> lambda) const
+{
+ for (const auto& configuration : _configurations) {
+ lambda(configuration.first);
+ }
+}
+
+void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first)
+{
+ auto& sources = _projects[project].sources;
+ if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
+ if (first) {
+ sources.insert(sources.begin(), source);
+ } else {
+ sources.push_back(source);
+ }
+ }
+}
+
+const std::string Manifest::projectPath(const std::string& projectName)
+{
+ auto project = _projects.find(projectName);
+ if (project == _projects.end())
+ return "";
+ if (project->second.sources.size() == 0)
+ return "";
+ return project->second.sources[0];
+}
+
+const bool Manifest::empty(void)
+{
+ for (const auto& configuration : _configurations) {
+ if (configuration.second.architectures.size() != 0)
+ return false;
+ }
+ return true;
+}
+
+const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; };
+void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
+
+const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
+void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
+
+const std::string Manifest::metabomFile() const { return _metabomFile; };
+void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
+
+const Platform Manifest::platform() const { return _platform; };
+void Manifest::setPlatform(const Platform platform) { _platform = platform; };
+
+const std::string& Manifest::build() const { return _build; };
+void Manifest::setBuild(const std::string& build) { _build = build; };
+const uint32_t Manifest::version() const { return _manifestVersion; };
+void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
+
+BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose)
+{
+ dyld3::BuildQueueEntry retval;
+
+ DyldSharedCache::CreateOptions options;
+ options.archName = arch;
+ options.platform = platform();
+ options.excludeLocalSymbols = true;
+ options.optimizeStubs = optimizeStubs;
+ options.optimizeObjC = true;
+ options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
+ DyldSharedCache::Agile : DyldSharedCache::SHA256only;
+ options.dylibsRemovedDuringMastering = true;
+ options.inodesAreSameAsRuntime = false;
+ options.cacheSupportsASLR = true;
+ options.forSimulator = false;
+ options.verbose = verbose;
+ options.evictLeafDylibsOnOverflow = false;
+ options.loggingPrefix = prefix;
+ options.pathPrefixes = { "" };
+ options.dylibOrdering = loadOrderFile(_dylibOrderFile);
+ options.dirtyDataSegmentOrdering = loadOrderFile(_dirtyDataOrderFile);
+
+ dyld3::BuildQueueEntry queueEntry;
+ retval.configNames = configs;
+ retval.options = options;
+ retval.outputPath = outputPath;
+ retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
+ retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
+ retval.mainExecutables = mainExecutables(*configs.begin(), arch);
+
+ return retval;
+}
+
+bool Manifest::loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures)
+{
+ const mach_header* mh = reinterpret_cast<const mach_header*>(p);
+ if (!MachOParser::isValidMachO(_diags, "", _platform, p, size, runtimePath.c_str(), false)) {
+ return false;
+ }
+
+ auto parser = MachOParser(mh);
+ if (_diags.hasError()) {
+ // Clear the error and punt
+ _diags.verbose("MachoParser error: %s\n", _diags.errorMessage().c_str());
+ _diags.clearError();
+ return false;
+ }
+
+ auto uuid = parser.uuid();
+ auto archName = parser.archName();
+
+ if (parser.fileType() == MH_DYLIB && architectures.count(parser.archName()) != 0) {
+ std::string installName = parser.installName();
+ auto index = std::make_pair(installName, parser.archName());
+ auto i = _installNameMap.find(index);
+
+ if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
+ || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
+ // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+ } else if (i == _installNameMap.end()) {
+ _installNameMap.insert(std::make_pair(index, uuid));
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+ if (installName[0] != '@' && installName != runtimePath) {
+ _diags.warning("Dylib located at '%s' has installname '%s'", runtimePath.c_str(), installName.c_str());
+ }
+ } else {
+ auto info = infoForUUID(i->second);
+ _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
+
+ // This is the "Good" one, overwrite
+ if (runtimePath == installName) {
+ _uuidMap.erase(uuid);
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+ }
+ }
+ } else {
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, "")));
+ }
+ return true;
+}
+
+//FIXME: assert we have not errored first
+bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
+{
+ __block bool retval = false;
+ const void* p = (uint8_t*)(-1);
+ struct stat stat_buf;
+
+ std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
+
+ if (p == (uint8_t*)(-1)) {
+ return false;
+ }
+
+ if (FatUtil::isFatFile(p)) {
+ FatUtil::forEachSlice(_diags, p, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+ if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
+ retval = true;
+ });
+ } else {
+ return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
+ }
+ return retval;
+}
+
+const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
+ auto i = _uuidMap.find(uuid);
+ assert(i != _uuidMap.end());
+ return i->second;
+}
+
+const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const {
+ UUIDInfo retval;
+ auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
+ if (uuidI == _installNameMap.end())
+ return UUIDInfo();
+
+ auto i = _uuidMap.find(uuidI->second);
+ if (i == _uuidMap.end())
+ return UUIDInfo();
+ return i->second;
+}
+
+MachOParser Manifest::parserForUUID(const UUID& uuid) const {
+ return MachOParser(infoForUUID(uuid).mh);
+}
+
+const std::string Manifest::buildPathForUUID(const UUID& uuid) {
+ return infoForUUID(uuid).buildPath;
+}
+
+const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
+ return infoForUUID(uuid).runtimePath;
+}
+
+Manifest::Manifest(Diagnostics& D, const std::string& path) : Manifest(D, path, std::set<std::string>())
+{
+}
+
+Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays) :
+ _diags(D)
+{
+ NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
+ NSString* platStr = manifestDict[@"platform"];
+ std::set<std::string> architectures;
+
+ if (platStr == nullptr)
+ platStr = @"ios";
+ std::string platformString = [platStr UTF8String];
+ setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
+
+ if (platformString == "ios") {
+ setPlatform(dyld3::Platform::iOS);
+ } else if ( (platformString == "tvos") || (platformString == "atv") ) {
+ setPlatform(dyld3::Platform::tvOS);
+ } else if ( (platformString == "watchos") || (platformString == "watch") ) {
+ setPlatform(dyld3::Platform::watchOS);
+ } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
+ setPlatform(dyld3::Platform::bridgeOS);
+ } else if ( (platformString == "macos") || (platformString == "osx") ) {
+ setPlatform(dyld3::Platform::macOS);
+ } else {
+ //Fixme should we error?
+ setPlatform(dyld3::Platform::iOS);
+ }
+
+ for (NSString* project in manifestDict[@"projects"]) {
+ for (NSString* source in manifestDict[@"projects"][project]) {
+ addProjectSource([project UTF8String], [source UTF8String]);
+ }
+ }
+
+ for (NSString* configuration in manifestDict[@"configurations"]) {
+ std::string configStr = [configuration UTF8String];
+ std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
+
+ 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].metabomTags.insert(configTag);
+ _configurations[configStr].platformName =
+ [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
+
+ if (endsWith(configStr, "InternalOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
+ } else if (endsWith(configStr, "VendorOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
+ } else if (endsWith(configStr, "VendorUIOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
+ } else if (endsWith(configStr, "CarrierOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
+ } else if (endsWith(configStr, "FactoryOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
+ } else if (endsWith(configStr, "DesenseOS")) {
+ _configurations[configStr].disposition = "internal";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
+ } else if (endsWith(configStr, "MinosOS")) {
+ _configurations[configStr].disposition = "minos";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
+ } else if (endsWith(configStr, "DemoOS")) {
+ _configurations[configStr].disposition = "demo";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
+ } else if (endsWith(configStr, "MinosOS")) {
+ _configurations[configStr].disposition = "minos";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
+ } else if (endsWith(configStr, "DeveloperOS")) {
+ _configurations[configStr].disposition = "user";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
+ } else if (endsWith(configStr, "OS")) {
+ _configurations[configStr].disposition = "user";
+ _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
+ }
+
+ for (NSString* architecutre in manifestDict[@"configurations"][configuration][@"architectures"]) {
+ //HACK until B&I stops mastering armv7s
+ if ([architecutre isEqual:@"armv7s"]) break;
+ _configurations[configStr].architectures[[architecutre UTF8String]] = Architecture();
+ architectures.insert([architecutre UTF8String]);
+ }
+ }
+
+ setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
+ setBuild([manifestDict[@"build"] UTF8String]);
+ if (manifestDict[@"dylibOrderFile"]) {
+ setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
+ }
+ if (manifestDict[@"dirtyDataOrderFile"]) {
+ setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
+ }
+
+ auto metabom = MBMetabomOpen(metabomFile().c_str(), false);
+ auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
+ MBEntry entry;
+
+ // FIXME error handling (NULL metabom)
+
+ //First we iterate through the bom and build our objects
+
+ while ((entry = MBIteratorNext(metabomEnumerator))) {
+ BOMFSObject fsObject = MBEntryGetFSObject(entry);
+ BOMFSObjType entryType = BOMFSObjectType(fsObject);
+ std::string entryPath = BOMFSObjectPathName(fsObject);
+ if (entryPath[0] == '.') {
+ entryPath.erase(0, 1);
+ }
+
+ // Skip artifacts that happen to be in the build chain
+ if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
+ continue;
+ }
+
+ // Skip variants we can't deal with
+ if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
+ continue;
+ }
+
+ // Skip images that are only used in InternalOS variants
+ if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
+ continue;
+ }
+
+ // Skip genCache generated dylibs
+ if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
+ continue;
+ }
+
+ MBTag tag;
+ auto tagCount = MBEntryGetNumberOfProjectTags(entry);
+ if (entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
+ 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 + "'";
+ }
+ _diags.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);
+ 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]));
+ }
+
+ _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
+ bool foundParser = false;
+ for (const auto& overlay : overlays) {
+ if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
+ foundParser = true;
+ break;
+ }
+ }
+
+ if (!foundParser) {
+ (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+ }
+ }
+ }
+
+ MBIteratorFree(metabomEnumerator);
+ MBMetabomFree(metabom);
+}
+
+void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
+ auto info = infoForUUID(imageInfo.uuid);
+ auto runtimePath = info.runtimePath;
+ mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
+{
+ std::vector<DyldSharedCache::MappedMachO> retval;
+ const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
+ for (const auto& dylib : dylibs) {
+ if (dylib.second.included) {
+ insert(retval, dylib.second);
+ }
+ }
+ return retval;
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
+{
+ std::vector<DyldSharedCache::MappedMachO> retval;
+ const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
+ for (const auto& dylib : dylibs) {
+ if (!dylib.second.included) {
+ insert(retval, dylib.second);
+ }
+ }
+
+ const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
+ for (const auto& bundle : bundles) {
+ insert(retval, bundle.second);
+ }
+
+ return retval;
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
+{
+ std::vector<DyldSharedCache::MappedMachO> retval;
+ const auto& executables = _configurations[configuration].architectures[architecture].results.executables;
+ for (const auto& executable : executables) {
+ insert(retval, executable.second);
+ }
+
+ return retval;
+}
+
+bool Manifest::filterForConfig(const std::string& configName)
+{
+ for (const auto configuration : _configurations) {
+ if (configName == configuration.first) {
+ std::map<std::string, Configuration> filteredConfigs;
+ filteredConfigs[configName] = configuration.second;
+
+ _configurations = filteredConfigs;
+
+ for (auto& arch : configuration.second.architectures) {
+ arch.second.results = Manifest::Results();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void Manifest::dedupeDispositions(void) {
+ // Since this is all hacky and inference based for now only do it for iOS until XBS
+ // is reved to give us real info. All the other platforms are way smaller anyway.
+ if (_platform != Platform::iOS)
+ return;
+
+ std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
+
+ for (const auto& configuration : _configurations) {
+ dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
+ }
+
+ for (const auto& dSet : dispositionSets) {
+ for (const auto &c1 : dSet.second) {
+ for (const auto &c2 : dSet.second) {
+ _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
+ }
+ }
+ }
+}
+
+void Manifest::calculateClosure()
+{
+ auto closureSemaphore = dispatch_semaphore_create(32);
+ auto closureGroup = dispatch_group_create();
+ auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
+
+ dedupeDispositions();
+ for (auto& config : _configurations) {
+ for (auto& arch : config.second.architectures) {
+ dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
+ dispatch_group_async(closureGroup, closureQueue, [&] {
+ calculateClosure(config.first, arch.first);
+ dispatch_semaphore_signal(closureSemaphore);
+ });
+ }
+ }
+
+ dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
+}
+
+void Manifest::remove(const std::string& config, const std::string& arch)
+{
+ if (_configurations.count(config))
+ _configurations[config].architectures.erase(arch);
+}
+
+void Manifest::removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration,
+ const std::string& architecture, std::unordered_set<UUID>& processedIdentifiers)
+{
+#if 0
+ 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(parser->uuid()) == 0) {
+ archManifest.results.dylibs[parser->uuid()].uuid = parser->uuid();
+ archManifest.results.dylibs[parser->uuid()].installname = parser->installName();
+ processedIdentifiers.insert(parser->uuid());
+ }
+ archManifest.results.exclude(MachOProxy::forIdentifier(parser->uuid(), architecture), reason);
+
+ processedIdentifiers.insert(parser->uuid());
+
+ for (const auto& dependent : proxy->dependentIdentifiers) {
+ auto dependentProxy = MachOProxy::forIdentifier(dependent, architecture);
+ auto dependentResultIter = archManifest.results.dylibs.find(dependentProxy->identifier);
+ if ( dependentProxy &&
+ ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
+ removeDylib(dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
+ processedIdentifiers);
+ }
+ }
+#endif
+}
+
+const std::string Manifest::removeLargestLeafDylib(const std::set<std::string>& configurations, const std::string& architecture)
+{
+ // Find the leaf nodes
+ __block std::map<std::string, uint64_t> dependentCounts;
+ for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) {
+ if (!dylib.second.included)
+ continue;
+ std::string installName;
+ auto info = infoForUUID(dylib.first);
+ auto parser = MachOParser(info.mh);
+ dependentCounts[parser.installName()] = 0;
+ }
+
+ for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) {
+ if (!dylib.second.included)
+ continue;
+ auto info = infoForUUID(dylib.first);
+ auto parser = MachOParser(info.mh);
+ parser.forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ if (!isWeak) {
+ dependentCounts[loadPath]++;
+ }
+ });
+ }
+
+ // Figure out which leaf is largest
+ UUIDInfo largestLeaf;
+
+ for (const auto& dependentCount : dependentCounts) {
+ if (dependentCount.second == 0) {
+ auto info = infoForInstallNameAndarch(dependentCount.first, architecture);
+ assert(info.mh != nullptr);
+ if (info.size > largestLeaf.size) {
+ largestLeaf = info;
+ }
+ }
+ }
+
+ if (largestLeaf.mh == nullptr) {
+ _diags.error("Fatal overflow, could not evict more dylibs");
+ return "";
+ }
+
+ // Remove it ferom all configs
+ for (const auto& config : configurations) {
+ configuration(config).architecture(architecture).results.exclude(*this, largestLeaf.uuid, "Cache Overflow");
+ }
+
+ return largestLeaf.installName;
+}
+
+void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
+{
+ __block auto& configManifest = _configurations[configuration];
+ __block auto& archManifest = _configurations[configuration].architectures[architecture];
+ __block std::set<UUID> newUuids;
+ std::set<UUID> processedUuids;
+ std::set<UUID> cachedUUIDs;
+
+ // Seed anchors
+ for (auto& uuidInfo : _uuidMap) {
+ auto info = uuidInfo.second;
+ if (info.arch != architecture) {
+ continue;
+ }
+
+ auto i = _metabomTagMap.find(info.runtimePath);
+ assert(i != _metabomTagMap.end());
+ auto tags = i->second;
+ if (!is_disjoint(tags, configManifest.metabomTags)) {
+ newUuids.insert(info.uuid);
+
+ }
+ }
+
+ // Pull in all dependencies
+ while (!newUuids.empty()) {
+ std::set<UUID> uuidsToProcess = newUuids;
+ newUuids.clear();
+
+ for (const auto& uuid : uuidsToProcess) {
+ if (processedUuids.count(uuid) > 0) {
+ continue;
+ }
+ processedUuids.insert(uuid);
+
+ auto parser = parserForUUID(uuid);
+ auto runtimePath = runtimePathForUUID(uuid);
+ assert(parser.header() != 0);
+
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
+ if (i != _installNameMap.end())
+ newUuids.insert(i->second);
+ });
+
+ if (parser.fileType() == MH_DYLIB) {
+ // Add the dylib to the results
+ if (archManifest.results.dylibs.count(uuid) == 0 ) {
+ archManifest.results.dylibs[uuid].uuid = uuid;
+ archManifest.results.dylibs[uuid].installname = parser.installName();
+ }
+
+ // HACK to insert device specific dylib closures into all caches
+ if ( parser.installName() == std::string("/System/Library/Caches/com.apple.xpc/sdk.dylib")
+ || parser.installName() == std::string("/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") ) {
+ archManifest.results.exclude(&parser, "Device specific dylib");
+ continue;
+ }
+
+ std::set<std::string> reasons;
+ if (parser.canBePlacedInDyldCache(runtimePath, reasons)) {
+ auto i = _metabomTagMap.find(runtimePath);
+ assert(i != _metabomTagMap.end());
+ auto restrictions = _metabomRestrictedTagMap.find(configuration);
+ if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+ archManifest.results.exclude(&parser, "Dylib '" + runtimePath + "' removed due to explict restriction");
+ }
+
+ // It can be placed in the cache, grab its dependents and queue them for inclusion
+ cachedUUIDs.insert(parser.uuid());
+ } else {
+ // It can't be placed in the cache, print out the reasons why
+ std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
+ for (auto i = reasons.begin(); i != reasons.end(); ++i) {
+ reasonString += *i;
+ if (i != --reasons.end()) {
+ reasonString += "\", \"";
+ }
+ }
+ reasonString += "\")";
+ archManifest.results.exclude(&parser, reasonString);
+ }
+ } else if (parser.fileType() == MH_BUNDLE) {
+ if (archManifest.results.bundles.count(uuid) == 0) {
+ archManifest.results.bundles[uuid].uuid = uuid;
+ }
+ } else if (parser.fileType() == MH_EXECUTE) {
+ //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
+ if (runtimePath == "/sbin/launchd"
+ || runtimePath == "/usr/local/sbin/launchd.debug"
+ || runtimePath == "/usr/local/sbin/launchd.development"
+ || runtimePath == "/usr/libexec/installd") {
+ continue;
+ }
+ if (archManifest.results.executables.count(uuid) == 0) {
+ archManifest.results.executables[uuid].uuid = uuid;
+ }
+ }
+ }
+ }
+
+ __block std::set<UUID> removedUUIDs;
+ __block bool doAgain = true;
+
+ //Trim out dylibs that are missing dependencies
+ while ( doAgain ) {
+ doAgain = false;
+ for (const auto& uuid : cachedUUIDs) {
+ __block std::set<std::string> badDependencies;
+ __block auto parser = parserForUUID(uuid);
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if (isWeak)
+ return;
+
+ auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
+ if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
+ removedUUIDs.insert(uuid);
+ badDependencies.insert(loadPath);
+ doAgain = true;
+ }
+
+ if (badDependencies.size()) {
+ std::string reasonString = "Rejected from cached dylibs: " + std::string(parser.installName()) + " " + architecture + " (\"";
+ for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
+ reasonString += *i;
+ if (i != --badDependencies.end()) {
+ reasonString += "\", \"";
+ }
+ }
+ reasonString += "\")";
+ archManifest.results.exclude(&parser, reasonString);
+ }
+ });
+ }
+
+ for (const auto& removedUUID : removedUUIDs) {
+ cachedUUIDs.erase(removedUUID);
+ }
+ }
+
+ //Trim out excluded leaf dylibs
+ __block std::set<std::string> linkedDylibs;
+
+ for(const auto& uuid : cachedUUIDs) {
+ auto parser = parserForUUID(uuid);
+ parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ linkedDylibs.insert(loadPath);
+ });
+ }
+
+ for(const auto& uuid : cachedUUIDs) {
+ auto info = infoForUUID(uuid);
+ auto i = _metabomTagMap.find(info.runtimePath);
+ assert(i != _metabomTagMap.end());
+ auto exclusions = _metabomExcludeTagMap.find(configuration);
+ if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
+ continue;
+
+ if (linkedDylibs.count(info.installName) != 0)
+ continue;
+
+ archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
+ }
+}
+
+void Manifest::writeJSON(const std::string& path) {
+ NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
+ for (auto& configuration : _configurations) {
+ jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
+
+ for (auto& arch : configuration.second.architectures) {
+ NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
+ NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
+ NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
+ for (auto& dylib : arch.second.results.dylibs) {
+ NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
+ if (dylib.second.included) {
+ [includedDylibsSet addObject:runtimePath];
+ } else {
+ [otherSet addObject:runtimePath];
+ }
+ }
+
+ for (auto& executable : arch.second.results.executables) {
+ NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
+ [executablesSet addObject:runtimePath];
+ }
+
+ for (auto& bundle : arch.second.results.bundles) {
+ NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
+ [otherSet addObject:runtimePath];
+ }
+
+ [includedDylibsSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
+ return [obj1 compare:obj2];
+ }];
+
+ [executablesSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
+ return [obj1 compare:obj2];
+ }];
+
+ [otherSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
+ return [obj1 compare:obj2];
+ }];
+
+ jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
+ }
+ }
+
+ NSError* error = nil;
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
+ (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
+}
+
+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"] = @(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) {
+ dylibDict[@"included"] = @YES;
+ } else {
+ dylibDict[@"included"] = @NO;
+ dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
+ }
+ dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
+ }
+
+ 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;
+ }
+
+ switch (platform()) {
+ case Platform::iOS:
+ cacheDict[@"platform"] = @"ios";
+ break;
+ case Platform::tvOS:
+ cacheDict[@"platform"] = @"tvos";
+ break;
+ case Platform::watchOS:
+ cacheDict[@"platform"] = @"watchos";
+ break;
+ case Platform::bridgeOS:
+ cacheDict[@"platform"] = @"bridgeos";
+ break;
+ case Platform::macOS:
+ cacheDict[@"platform"] = @"macos";
+ break;
+ case Platform::unknown:
+ cacheDict[@"platform"] = @"unknown";
+ break;
+ }
+
+ 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
+/* -*- 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, Diagnostics& diag) {
+ if (sizeof(*this) < offsetof(objc_protocol_t<P>, demangledName) + sizeof(demangledName))
+ diag.error("objc protocol has the wrong size");
+ else
+ 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.
+ cache->diagnostics().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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "StringUtils.h"
+#include "Trie.hpp"
+#include "MachOFileAbstraction.hpp"
+#include "MachOParser.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.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
+};
+
+
+static uint64_t branchPoolTextSize(const std::string& archName)
+{
+ if ( startsWith(archName, "arm64") )
+ return 0x0000C000; // 48KB
+ else
+ return 0;
+}
+
+static uint64_t branchPoolLinkEditSize(const std::string& archName)
+{
+ if ( startsWith(archName, "arm64") )
+ return 0x00100000; // 1MB
+ else
+ return 0;
+}
+
+
+template <typename P>
+class BranchPoolDylib {
+public:
+ BranchPoolDylib(DyldSharedCache* cache, uint64_t startAddr,
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags);
+
+ 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:
+ Diagnostics& _diagnostics;
+ 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;
+
+ DyldSharedCache* _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(DyldSharedCache* cache, uint64_t poolStartAddr,
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags)
+ : _cacheBuffer(cache), _startAddr(poolStartAddr), _nextIndex(0), _firstStubOffset(0x280), _diagnostics(diags)
+{
+ std::string archName = cache->archName();
+ bool is64 = (sizeof(typename P::uint_t) == 8);
+
+ const uint64_t textSegSize = branchPoolTextSize(archName);
+ const uint64_t linkEditSegSize = branchPoolLinkEditSize(archName);
+ 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*)cache + poolStartAddr - textRegionStartAddr);
+ mh->set_magic(is64 ? MH_MAGIC_64 : MH_MAGIC);
+ mh->set_cputype(dyld3::MachOParser::cpuTypeFromArchName(archName));
+ mh->set_cpusubtype(dyld3::MachOParser::cpuSubtypeFromArchName(archName));
+ 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(archName));
+ 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*)cache + 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 ) {
+ _diagnostics.verbose("branch islands in image at 0x%0llX:\n", _startAddr);
+ for (uint32_t i=0; i < _nextIndex; ++i) {
+ _diagnostics.verbose(" 0x%llX %s\n", 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) {
+ _diagnostics.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) {
+ _diagnostics.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()
+{
+ _diagnostics.verbose(" island pool at 0x%0llX has %u stubs and stringPool size=%lu\n", _startAddr, _nextIndex, _nextString - _stringPoolStart);
+}
+
+
+
+template <typename P>
+class StubOptimizer {
+public:
+ StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diags);
+ 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:
+ Diagnostics _diagnostics;
+ 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, Diagnostics& diags)
+: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diags)
+{
+ _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 ) {
+ _diagnostics.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 ) {
+ _diagnostics.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 ) {
+ _diagnostics.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 ) {
+ _diagnostics.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 ) {
+ _diagnostics.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() ) {
+ _diagnostics.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() ) {
+ _diagnostics.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* 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]);
+ lpVMAddr = (pint_t)sect->addr() + j * sizeof(pint_t);
+ if ( symbolIndex >= _symTabCmd->nsyms() ) {
+ _diagnostics.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() ) {
+ _diagnostics.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) ) {
+ _diagnostics.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;
+ }
+ }
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+template <typename P>
+void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
+{
+ if (_diagnostics.hasError())
+ return;
+ const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+ const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
+ if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
+ _diagnostics.error("malformed split seg info in %s", _installName);
+ return;
+ }
+
+ 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) {
+ _diagnostics.error("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)) ) {
+ _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _installName);
+ return 0;
+ }
+ 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 ) {
+ _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName);
+ return 0;
+ }
+ }
+ else {
+ _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _installName);
+ return 0;
+ }
+ 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 ){
+ _diagnostics.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 ) {
+ _diagnostics.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);
+ if (_diagnostics.hasError())
+ return false;
+ _branchesDirectCount++;
+ return true;
+ }
+ }
+ else if ( kind == DYLD_CACHE_ADJ_V2_ARM_BR24 ) {
+ // too few of these to be worth trying to optimize
+ }
+
+ return false;
+ });
+ if (_diagnostics.hasError())
+ return;
+}
+
+
+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 ) {
+ _diagnostics.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
+ _diagnostics.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
+ _diagnostics.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;
+ });
+ if (_diagnostics.hasError())
+ return;
+}
+
+
+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 ) {
+ _diagnostics.verbose("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s\n",
+ _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName);
+ }
+ break;
+ case CPU_TYPE_ARM:
+ optimizeArmCallSites();
+ optimizeArmStubs();
+ if ( verbose ) {
+ _diagnostics.verbose("%3u of %3u stubs optimized. %5u branches in __text, %5u changed to direct branches for %s\n",
+ _stubOptimizedCount, _stubCount, _branchesCount, _branchesDirectCount, _installName);
+ }
+ break;
+ }
+}
+
+template <typename P>
+void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std::vector<uint64_t>& branchPoolStartAddrs,
+ const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+{
+ diags.verbose("Stub elimination optimization:\n");
+
+ // construct a StubOptimizer for each image
+ __block std::vector<StubOptimizer<P>*> optimizers;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ optimizers.push_back(new StubOptimizer<P>((void*)cache, (macho_header<P>*)mh, diags));
+ });
+
+ // construct a BranchPoolDylib for each pool
+ std::vector<BranchPoolDylib<P>*> pools;
+
+ if ( startsWith(archName, "arm64") ) {
+ // Find hole at end of linkedit region for branch pool linkedits
+ __block uint64_t textRegionStartAddr = 0;
+ __block uint64_t linkEditRegionStartAddr = 0;
+ __block uint64_t linkEditRegionEndAddr = 0;
+ __block uint64_t linkEditRegionStartCacheOffset = 0;
+ cache->forEachRegion(^(const 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*)cache;
+ }
+ });
+ __block uint64_t lastLinkEditRegionUsedOffset = 0;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ dyld3::MachOParser parser(mh);
+ parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+ if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+ if ( fileOffset >= lastLinkEditRegionUsedOffset )
+ lastLinkEditRegionUsedOffset = fileOffset + vmSize;
+ }
+ });
+ });
+ 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>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset, diags));
+ 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* const* d=neverStubEliminateDylibs; *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) ) {
+ diags.error("malformed exports trie in %s", *d);
+ return;
+ }
+ 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;
+ }
+ diags.verbose(" cache contains %u call sites of which %u were direct bound and %u were bound through islands\n", callSiteCount, callSiteDirectOptCount, callSiteOneHopOptCount);
+
+ // clean up
+ for (StubOptimizer<P>* op : optimizers)
+ delete op;
+ for (BranchPoolDylib<P>* p : pools)
+ delete p;
+
+}
+
+void bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+{
+ std::string archName = cache->archName();
+ if ( startsWith(archName, "arm64") )
+ bypassStubs<Pointer64<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+ else if ( archName == "armv7k" )
+ bypassStubs<Pointer32<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+ // no stub optimization done for other arches
+}
+
+
+/*
+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@
+ */
+
+
+#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 <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "MachOFileAbstraction.hpp"
+#include "Trie.hpp"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+
+
+#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;
+ }
+
+ size_t size() {
+ size_t size = 1;
+ for (auto& entry : _map) {
+ size += (entry.first.size() + 1);
+ }
+ return size;
+ }
+
+
+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, Diagnostics& diag);
+
+ 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;
+ Diagnostics& _diagnostics;
+ 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(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, 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, Diagnostics& diag, 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<dyld_cache_image_info_extra> _extraInfo;
+ std::vector<uint8_t> _trieBytes;
+ std::vector<uint16_t> _reExportArray;
+ std::vector<uint16_t> _dependencyArray;
+ std::vector<uint16_t> _bottomUpArray;
+ std::vector<dyld_cache_accelerator_initializer> _initializers;
+ std::vector<dyld_cache_accelerator_dof> _dofSections;
+ std::vector<dyld_cache_range_entry> _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;
+ dyld_cache_accelerator_info _acceleratorInfoHeader;
+};
+
+
+template <typename P>
+void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
+ 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 == target ) {
+ foundCycle = true;
+ break;
+ }
+ }
+ 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;
+ }
+ diag.warning("%s", msg.c_str());
+ return;
+ }
+
+ if ( visitedNodes.count(node) )
+ continue;
+ visitedNodes.insert(node);
+ NodeChain nextChain;
+ nextChain.prev = &chain;
+ nextChain.node = node;
+ verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
+ }
+}
+
+const uint16_t kBranchIslandDylibIndex = 0x7FFF;
+
+template <typename P>
+AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, 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;
+ }
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
+ uint64_t cacheStartAddress = mappings[0].address;
+ const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
+ for (unsigned i=0; i < cache->header.imagesCount; ++i) {
+ uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
+ macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
+ const char* path = (char*)cache + 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;
+ }
+
+
+ // 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, diag, 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
+ dyld_cache_image_info_extra emptyExtra;
+ emptyExtra.exportsTrieAddr = 0;
+ emptyExtra.weakBindingsAddr = 0;
+ emptyExtra.exportsTrieSize = 0;
+ emptyExtra.weakBindingsSize = 0;
+ emptyExtra.dependentsStartArrayIndex = 0;
+ emptyExtra.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].dependentsStartArrayIndex = 0;
+ }
+ else {
+ _extraInfo[index].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].reExportsStartArrayIndex = 0;
+ }
+ else {
+ _extraInfo[index].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());
+ dyld_cache_accelerator_initializer entry;
+ entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
+ entry.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());
+ dyld_cache_accelerator_dof entry;
+ entry.sectionAddress = sect->addr();
+ entry.sectionSize = (uint32_t)sect->size();
+ entry.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].exportsTrieAddr = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
+ _extraInfo[index].exportsTrieSize = op->exportsTrieLinkEditSize();
+ _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
+ _extraInfo[index].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;
+ dyld_cache_range_entry entry;
+ entry.startAddress = segCmd->vmaddr();
+ entry.size = (uint32_t)segCmd->vmsize();
+ entry.imageIndex = imageIndex;
+ _rangeTable.push_back(entry);
+ }
+ }
+ std::sort(_rangeTable.begin(), _rangeTable.end(),
+ [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& 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
+ _acceleratorInfoHeader.version = 1;
+ _acceleratorInfoHeader.imageExtrasCount = (uint32_t)_extraInfo.size();
+ _acceleratorInfoHeader.imagesExtrasOffset = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
+ _acceleratorInfoHeader.bottomUpListOffset = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
+ _acceleratorInfoHeader.dylibTrieOffset = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
+ _acceleratorInfoHeader.dylibTrieSize = (uint32_t)_trieBytes.size();
+ _acceleratorInfoHeader.initializersOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
+ _acceleratorInfoHeader.initializersCount = (uint32_t)_initializers.size();
+ _acceleratorInfoHeader.dofSectionsOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
+ _acceleratorInfoHeader.dofSectionsCount = (uint32_t)_dofSections.size();
+ _acceleratorInfoHeader.reExportListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
+ _acceleratorInfoHeader.reExportCount = (uint32_t)_reExportArray.size();
+ _acceleratorInfoHeader.depListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
+ _acceleratorInfoHeader.depListCount = (uint32_t)_dependencyArray.size();
+ _acceleratorInfoHeader.rangeTableOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
+ _acceleratorInfoHeader.rangeTableCount = (uint32_t)_rangeTable.size();
+ _acceleratorInfoHeader.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, Diagnostics& diag)
+: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
+{
+ _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(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers, Diagnostics& diagnostics, dyld_cache_local_symbols_info** localsInfo)
+{
+ // 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;
+
+ diagnostics.verbose("Merged LINKEDIT:\n");
+
+ // copy weak binding info
+ uint32_t startWeakBindInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyWeakBindingInfo(newLinkEdit, offset);
+ }
+ diagnostics.verbose(" weak bindings size: %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
+
+ // copy export info
+ uint32_t startExportInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyExportInfo(newLinkEdit, offset);
+ }
+ diagnostics.verbose(" exports info size: %5uKB\n", (uint32_t)(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);
+ }
+ diagnostics.verbose(" bindings size: %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
+
+ // copy lazy binding info
+ uint32_t startLazyBindingsInfosOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyLazyBindingInfo(newLinkEdit, offset);
+ }
+ diagnostics.verbose(" lazy bindings size: %5uKB\n", (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);
+ }
+ diagnostics.verbose(" function starts size: %5uKB\n", (offset-startFunctionStartsOffset)/1024);
+
+ // copy data-in-code info
+ uint32_t startDataInCodeOffset = offset;
+ for (LinkeditOptimizer<P>* op : optimizers) {
+ op->copyDataInCode(newLinkEdit, offset);
+ }
+ diagnostics.verbose(" data in code size: %5uKB\n", (offset-startDataInCodeOffset)/1024);
+
+ // 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);
+ diagnostics.verbose(" symbol table size: %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+ diagnostics.verbose(" symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
+
+ // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
+ diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+ ::memcpy((char*)cache + linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
+ ::bzero((char*)cache + linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
+ ::free(newLinkEdit);
+
+ // If making cache for customers, add extra accelerator tables for dyld
+ if ( addAcceleratorTables ) {
+ AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
+ uint32_t tablesSize = tables.totalSize();
+ if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
+ tables.copyTo((uint8_t*)cache+newLinkeditEnd);
+ newLinkeditEnd += tablesSize;
+ uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
+ cache->header.accelerateInfoAddr = accelInfoAddr;
+ cache->header.accelerateInfoSize = tablesSize;
+ diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
+ }
+ else {
+ diagnostics.warning("not enough room to add dyld accelerator tables");
+ }
+ }
+
+ // update mapping to reduce linkedit size
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+ mappings[2].size = newLinkeditEnd - mappings[2].fileOffset;
+
+ // 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;
+ const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
+ const uint32_t entriesCount = (uint32_t)localSymbolInfos.size();
+ const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyld_cache_local_symbols_info), 4); // 16-byte align start
+ const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size();
+ const uint32_t stringsSize = (uint32_t)localSymbolsStringPool.size();
+ const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
+ // allocate buffer for local symbols
+ const size_t localsBufferSize = align(stringsOffset + stringsSize, 14);
+ dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)malloc(localsBufferSize);
+ // fill in header info
+ infoHeader->nlistOffset = nlistOffset;
+ infoHeader->nlistCount = nlistCount;
+ infoHeader->stringsOffset = stringsOffset;
+ infoHeader->stringsSize = stringsSize;
+ infoHeader->entriesOffset = entriesOffset;
+ infoHeader->entriesCount = entriesCount;
+ // copy info for each dylib
+ dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset);
+ for (int i=0; i < entriesCount; ++i) {
+ entries[i].dylibOffset = localSymbolInfos[i].dylibOffset;
+ entries[i].nlistStartIndex = localSymbolInfos[i].nlistStartIndex;
+ entries[i].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
+ localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+ // return buffer of local symbols, caller to free() it
+ *localsInfo = infoHeader;
+ }
+
+ // 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>
+uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
+{
+ // construct a LinkeditOptimizer for each image
+ __block std::vector<LinkeditOptimizer<P>*> optimizers;
+ cache->forEachImage(^(const mach_header* mh, const char*) {
+ optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, diag));
+ });
+#if 0
+ // add optimizer for each branch pool
+ for (uint64_t poolOffset : branchPoolOffsets) {
+ macho_header<P>* mh = (macho_header<P>*)((char*)cache + poolOffset);
+ optimizers.push_back(new LinkeditOptimizer<P>(cache, mh, diag));
+ }
+#endif
+ // merge linkedit info
+ uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
+
+ // delete optimizers
+ for (LinkeditOptimizer<P>* op : optimizers)
+ delete op;
+
+ return newFileSize;
+}
+
+uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
+{
+ if ( is64) {
+ return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+ }
+ else {
+ return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+ }
+}
+
+
+
--- /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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "CacheBuilder.h"
+#include "FileAbstraction.hpp"
+#include "MachOFileAbstraction.hpp"
+
+
+// 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(const DyldSharedCache* cache, Diagnostics& diag)
+ : _diagnostics(diag)
+ {
+ __block int index = 0;
+ cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ _regions[index++] = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size };
+ });
+ }
+
+ void* contentForVMAddr(uint64_t vmaddr) {
+ for (const Info& info : _regions) {
+ if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) )
+ return (void*)(info.contentStart + vmaddr - info.startAddr);
+ }
+ if ( vmaddr != 0 )
+ _diagnostics.error("invalid vmaddr 0x%0llX in ObjC data", vmaddr);
+ return nullptr;
+ }
+
+ uint64_t vmAddrForContent(const void* content) {
+ for (const Info& info : _regions) {
+ if ( (info.contentStart <= content) && (content < info.contentEnd) )
+ return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart);
+ }
+ _diagnostics.error("invalid content pointer %p in ObjC data", content);
+ return 0;
+ }
+
+ Diagnostics& diagnostics() { return _diagnostics; }
+
+private:
+ struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; };
+ Diagnostics& _diagnostics;
+ Info _regions[3];
+};
+
+
+// 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 ) {
+ _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+ return 0;
+ }
+ 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 ) {
+ _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+ return;
+ }
+ 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) {
+ _cache->diagnostics().error("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;
+ Diagnostics& _diagnostics;
+
+ 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(Diagnostics& diag)
+ : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag) {
+ }
+
+ 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>)) {
+ _diagnostics.error("objc protocol is too big");
+ return;
+ }
+
+ 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, _diagnostics);
+ }
+ 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(DyldSharedCache* cache, bool forProduction, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+ typedef typename P::E E;
+ typedef typename P::uint_t pint_t;
+
+ diag.verbose("Optimizing objc metadata:\n");
+ diag.verbose(" cache type is %s\n", forProduction ? "production" : "development");
+
+ ContentAccessor cacheAccessor(cache, diag);
+
+ size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
+ if (headerSize != sizeof(objc_opt::objc_opt_t)) {
+ diag.warning("libobjc's optimization structure size is wrong (metadata not optimized)");
+ }
+
+ //
+ // Find libobjc's empty sections and build list of images with objc metadata
+ //
+ __block const macho_section<P> *optROSection = nullptr;
+ __block const macho_section<P> *optRWSection = nullptr;
+ __block const macho_section<P> *optPointerListSection = nullptr;
+ __block std::vector<const macho_header<P>*> objcDylibs;
+ cache->forEachImage(^(const mach_header* machHeader, const char* installName) {
+ 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 ) {
+ diag.warning("libobjc's read-only section missing (metadata not optimized)");
+ return;
+ }
+ if ( optRWSection == nullptr ) {
+ diag.warning("libobjc's read/write section missing (metadata not optimized)");
+ return;
+ }
+ if ( optPointerListSection == nullptr ) {
+ diag.warning("libobjc's pointer list section missing (metadata not optimized)");
+ return;
+ }
+
+ uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr());
+ if ( optROData == nullptr ) {
+ diag.warning("libobjc's read-only section has bad content");
+ return;
+ }
+ size_t optRORemaining = optROSection->size();
+ uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr());
+ size_t optRWRemaining = optRWSection->size();
+ if (optRORemaining < headerSize) {
+ diag.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) {
+ diag.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>)) {
+ diag.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) {
+ diag.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) {
+ diag.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);
+ }
+
+ diag.verbose(" uniqued %6lu selectors\n", uniq.strings().size());
+ diag.verbose(" updated %6lu selector references\n", 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) {
+ diag.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;
+
+ diag.verbose(" selector table occupancy %u/%u (%u%%)\n",
+ 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) {
+ diag.error("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);
+ }
+
+ diag.verbose(" recorded % 6ld classes\n", 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) {
+ diag.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;
+
+ diag.verbose(" found % 6ld duplicate classes\n",
+ duplicateCount);
+ diag.verbose(" class table occupancy %u/%u (%u%%)\n",
+ 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);
+ }
+
+ diag.verbose(" sorted % 6ld method lists\n", 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(diag);
+ for (const macho_header<P>* mh : sizeSortedDylibs) {
+ protocolOptimizer.addProtocols(&cacheAccessor, mh);
+ }
+
+ diag.verbose(" uniqued % 6ld protocols\n",
+ protocolOptimizer.protocolCount());
+
+ pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
+ err = protocolOptimizer.writeProtocols(&cacheAccessor,
+ optRWData, optRWRemaining,
+ optROData, optRORemaining,
+ pointersForASLR, protocolClassVMAddr);
+ if (err) {
+ diag.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) {
+ diag.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;
+
+ diag.verbose(" protocol table occupancy %u/%u (%u%%)\n",
+ 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);
+ }
+
+ diag.verbose(" updated % 6ld protocol references\n", 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);
+ }
+
+ diag.verbose(" updated % 6ld ivar offsets\n", 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;
+ diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read-only optimization section\n",
+ roSize, optROSection->size(), percent(roSize, optROSection->size()));
+ diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read/write optimization section\n",
+ rwSize, optRWSection->size(), percent(rwSize, optRWSection->size()));
+ diag.verbose(" wrote objc metadata optimization version %d\n", objc_opt::VERSION);
+}
+
+
+} // anon namespace
+
+void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+ if ( is64 )
+ optimizeObjC<Pointer64<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+ else
+ optimizeObjC<Pointer32<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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 StringUtils_h
+#define StringUtils_h
+
+#include <string>
+
+inline bool startsWith(const std::string& str, const std::string& prefix)
+{
+ return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
+}
+
+inline bool endsWith(const std::string& str, const std::string& suffix)
+{
+ std::size_t index = str.find(suffix, str.size() - suffix.size());
+ return (index != std::string::npos);
+}
+
+inline bool contains(const std::string& str, const std::string& search)
+{
+ std::size_t index = str.find(search);
+ return (index != std::string::npos);
+}
+
+inline char hexDigit(uint8_t value)
+{
+ if ( value < 10 )
+ return '0' + value;
+ else
+ return 'a' + value - 10;
+}
+
+inline void bytesToHex(const uint8_t* bytes, size_t byteCount, char buffer[])
+{
+ char* p = buffer;
+ for (int i=0; i < byteCount; ++i) {
+ *p++ = hexDigit(bytes[i] >> 4);
+ *p++ = hexDigit(bytes[i] & 0x0F);
+ }
+ *p++ = '\0';
+}
+
+inline uint8_t hexCharToUInt(const char hexByte, uint8_t& value) {
+ if (hexByte >= '0' && hexByte <= '9') {
+ value = hexByte - '0';
+ return true;
+ } else if (hexByte >= 'A' && hexByte <= 'F') {
+ value = hexByte - 'A' + 10;
+ return true;
+ } else if (hexByte >= 'a' && hexByte <= 'f') {
+ value = hexByte - 'a' + 10;
+ return true;
+ }
+
+ return false;
+}
+
+inline uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte) {
+ if (startHexByte == nullptr)
+ return 0;
+ uint64_t retval = 0;
+ if (startHexByte[0] == '0' && startHexByte[1] == 'x') {
+ startHexByte +=2;
+ }
+ *endHexByte = startHexByte + 16;
+
+ //FIXME overrun?
+ for (uint32_t i = 0; i < 16; ++i) {
+ uint8_t value;
+ if (!hexCharToUInt(startHexByte[i], value)) {
+ *endHexByte = &startHexByte[i];
+ break;
+ }
+ retval = (retval << 4) + value;
+ }
+ return retval;
+}
+
+inline bool hexToBytes(const char* startHexByte, uint32_t length, uint8_t buffer[]) {
+ if (startHexByte == nullptr)
+ return false;
+ const char *currentHexByte = startHexByte;
+ for (uint32_t i = 0; i < length; ++i) {
+ uint8_t value;
+ if (!hexCharToUInt(currentHexByte[i], value)) {
+ return false;
+ }
+ if (i%2 == 0) {
+ buffer[i/2] = value << 4;
+ } else {
+ buffer[(i-1)/2] |= value;
+ }
+ }
+ return true;
+}
+
+#endif // StringUtils_h
+
--- /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 <mach-o/loader.h>
+
+#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() : address(0), flags(0), other(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; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006-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 __DYLD_CACHE_FORMAT__
+#define __DYLD_CACHE_FORMAT__
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+
+
+struct dyld_cache_header
+{
+ char magic[16]; // e.g. "dyld_v0 i386"
+ uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
+ uint32_t mappingCount; // number of dyld_cache_mapping_info entries
+ uint32_t imagesOffset; // file offset to first dyld_cache_image_info
+ uint32_t imagesCount; // number of dyld_cache_image_info entries
+ uint64_t dyldBaseAddress; // base address of dyld when cache was built
+ uint64_t codeSignatureOffset; // file offset of code signature blob
+ uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
+ uint64_t slideInfoOffset; // file offset of kernel slid info
+ uint64_t slideInfoSize; // size of kernel slid info
+ 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; // 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
+ uint64_t dylibsImageGroupAddr; // (unslid) address of ImageGroup for dylibs in this cache
+ uint64_t dylibsImageGroupSize; // size of ImageGroup for dylibs in this cache
+ uint64_t otherImageGroupAddr; // (unslid) address of ImageGroup for other OS dylibs
+ uint64_t otherImageGroupSize; // size of oImageGroup for other OS dylibs
+ uint64_t progClosuresAddr; // (unslid) address of list of program launch closures
+ uint64_t progClosuresSize; // size of list of program launch closures
+ uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures
+ uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures
+ uint32_t platform; // platform number (macOS=1, etc)
+ uint32_t formatVersion : 8, // launch_cache::binary_format::kFormatVersion
+ dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
+ simulator : 1; // for simulator of specified platform
+ uint64_t sharedRegionStart; // base load address of cache if not slid
+ uint64_t sharedRegionSize; // overall size of region cache can be mapped into
+ uint64_t maxSlide; // runtime slide of cache can be between zero and this value
+};
+
+
+struct dyld_cache_mapping_info {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t maxProt;
+ uint32_t initProt;
+};
+
+struct dyld_cache_image_info
+{
+ uint64_t address;
+ uint64_t modTime;
+ uint64_t inode;
+ uint32_t pathFileOffset;
+ 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
+ uint32_t toc_offset;
+ uint32_t toc_count;
+ uint32_t entries_offset;
+ uint32_t entries_count;
+ uint32_t entries_size; // currently 128
+ // uint16_t toc[toc_count];
+ // entrybitmap entries[entries_count];
+};
+
+
+// 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
+ uint32_t nlistCount; // count of nlist entries
+ uint32_t stringsOffset; // offset into this chunk of string pool
+ uint32_t stringsSize; // byte count of string pool
+ uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry
+ uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array
+};
+
+struct dyld_cache_local_symbols_entry
+{
+ uint32_t dylibOffset; // offset in cache file of start of dylib
+ uint32_t nlistStartIndex; // start index of locals for this dylib
+ uint32_t nlistCount; // number of local symbols for this dylib
+};
+
+
+
+#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;
+
+
+
+
+#endif // __DYLD_CACHE_FORMAT__
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 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>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+#include <mach-o/dyld_priv.h>
+#include <bootstrap.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+
+#include <map>
+#include <vector>
+
+#include "LaunchCache.h"
+#include "LaunchCacheWriter.h"
+#include "DyldSharedCache.h"
+#include "FileUtils.h"
+#include "ImageProxy.h"
+#include "StringUtils.h"
+#include "ClosureBuffer.h"
+
+extern "C" {
+ #include "closuredProtocol.h"
+}
+
+static const DyldSharedCache* mapCacheFile(const char* path)
+{
+ struct stat statbuf;
+ if (stat(path, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
+ return nullptr;
+ }
+
+ int cache_fd = open(path, O_RDONLY);
+ if (cache_fd < 0) {
+ fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
+ return nullptr;
+ }
+
+ void* mapped_cache = mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ if (mapped_cache == MAP_FAILED) {
+ fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
+ return nullptr;
+ }
+ close(cache_fd);
+
+ return (DyldSharedCache*)mapped_cache;
+}
+
+struct CachedSections
+{
+ uint32_t mappedOffsetStart;
+ uint32_t mappedOffsetEnd;
+ uint64_t vmAddress;
+ const mach_header* mh;
+ std::string segmentName;
+ std::string sectionName;
+ const char* dylibPath;
+};
+
+static const CachedSections& find(uint32_t mappedOffset, const std::vector<CachedSections>& sections)
+{
+ for (const CachedSections& entry : sections) {
+ //printf("0x%08X -> 0x%08X\n", entry.mappedOffsetStart, entry.mappedOffsetEnd);
+ if ( (entry.mappedOffsetStart <= mappedOffset) && (mappedOffset < entry.mappedOffsetEnd) )
+ return entry;
+ }
+ assert(0 && "invalid offset");
+}
+
+/*
+static const dyld3::launch_cache::BinaryClosureData*
+callClosureDaemon(const std::string& mainPath, const std::string& cachePath, const std::vector<std::string>& envArgs)
+{
+
+ mach_port_t serverPort = MACH_PORT_NULL;
+ mach_port_t bootstrapPort = MACH_PORT_NULL;
+ kern_return_t kr = task_get_bootstrap_port(mach_task_self(), &bootstrapPort);
+ kr = bootstrap_look_up(bootstrapPort, "com.apple.dyld.closured", &serverPort);
+ switch( kr ) {
+ case BOOTSTRAP_SUCCESS :
+ // service currently registered, "a good thing" (tm)
+ break;
+ case BOOTSTRAP_UNKNOWN_SERVICE :
+ // service not currently registered, try again later
+ fprintf(stderr, "bootstrap_look_up(): %s\n", mach_error_string(kr));
+ return nullptr;
+ default:
+ // service not currently registered, try again later
+ fprintf(stderr, "bootstrap_look_up(): %s [%d]\n", mach_error_string(kr), kr);
+ return nullptr;
+ }
+
+ //printf("serverPort=%d, replyPort=%d\n", serverPort, replyPort);
+
+
+
+ bool success;
+ char envBuffer[2048];
+ vm_offset_t reply = 0;
+ uint32_t replySize = 0;
+ envBuffer[0] = '\0';
+ envBuffer[1] = '\0';
+// kr = closured_CreateLaunchClosure(serverPort, mainPath.c_str(), cachePath.c_str(), uuid, envBuffer, &success, &reply, &replySize);
+
+ printf("success=%d, buf=%p, bufLen=%d\n", success, (void*)reply, replySize);
+
+ if (!success)
+ return nullptr;
+ return (const dyld3::launch_cache::BinaryClosureData*)reply;
+}
+*/
+
+static void usage()
+{
+ printf("dyld_closure_util program to create of view dyld3 closures\n");
+ printf(" mode:\n");
+ printf(" -create_closure <prog-path> # create a closure for the specified main executable\n");
+ printf(" -create_image_group <dylib-path> # create an ImageGroup for the specified dylib/bundle\n");
+ printf(" -list_dyld_cache_closures # list all closures in the dyld shared cache with size\n");
+ printf(" -list_dyld_cache_other_dylibs # list all group-1 (non-cached dylibs/bundles)\n");
+ printf(" -print_image_group <closure-path> # print specified ImageGroup file as JSON\n");
+ printf(" -print_closure_file <closure-path> # print specified closure file as JSON\n");
+ printf(" -print_dyld_cache_closure <prog-path> # find closure for specified program in dyld cache and print as JSON\n");
+ printf(" -print_dyld_cache_dylibs # print group-0 (cached dylibs) as JSON\n");
+ printf(" -print_dyld_cache_other_dylibs # print group-1 (non-cached dylibs/bundles) as JSON\n");
+ printf(" -print_dyld_cache_other <path> # print just one group-1 (non-cached dylib/bundle) as JSON\n");
+ printf(" -print_dyld_cache_patch_table # print locations in shared cache that may need patching\n");
+ printf(" options:\n");
+ printf(" -cache_file <cache-path> # path to cache file to use (default is current cache)\n");
+ printf(" -build_root <path-prefix> # when building a closure, the path prefix when runtime volume is not current boot volume\n");
+ printf(" -o <output-file> # when building a closure, the file to write the (binary) closure to\n");
+ printf(" -include_all_dylibs_in_dir # when building a closure, add other mach-o files found in directory\n");
+ printf(" -env <var=value> # when building a closure, DYLD_* env vars to assume\n");
+ printf(" -dlopen <path> # for use with -create_closure to append ImageGroup if target had called dlopen\n");
+ printf(" -verbose_fixups # for use with -print* options to force printing fixups\n");
+}
+
+int main(int argc, const char* argv[])
+{
+ const char* cacheFilePath = nullptr;
+ const char* inputMainExecutablePath = nullptr;
+ const char* inputTopImagePath = nullptr;
+ const char* outPath = nullptr;
+ const char* printPath = nullptr;
+ const char* printGroupPath = nullptr;
+ const char* printCacheClosure = nullptr;
+ const char* printCachedDylib = nullptr;
+ const char* printOtherDylib = nullptr;
+ bool listCacheClosures = false;
+ bool listOtherDylibs = false;
+ bool includeAllDylibs = false;
+ bool printClosures = false;
+ bool printCachedDylibs = false;
+ bool printOtherDylibs = false;
+ bool printPatchTable = false;
+ bool useClosured = false;
+ bool verboseFixups = false;
+ std::vector<std::string> buildtimePrefixes;
+ std::vector<std::string> envArgs;
+ std::vector<const char*> dlopens;
+
+ if ( argc == 1 ) {
+ usage();
+ return 0;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( strcmp(arg, "-cache_file") == 0 ) {
+ cacheFilePath = argv[++i];
+ if ( cacheFilePath == nullptr ) {
+ fprintf(stderr, "-cache_file option requires path to cache file\n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-create_closure") == 0 ) {
+ inputMainExecutablePath = argv[++i];
+ if ( inputMainExecutablePath == nullptr ) {
+ fprintf(stderr, "-create_closure option requires a path to an executable\n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-create_image_group") == 0 ) {
+ inputTopImagePath = argv[++i];
+ if ( inputTopImagePath == nullptr ) {
+ fprintf(stderr, "-create_image_group option requires a path to a dylib or bundle\n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-dlopen") == 0 ) {
+ const char* path = argv[++i];
+ if ( path == nullptr ) {
+ fprintf(stderr, "-dlopen option requires a path to a packed closure list\n");
+ return 1;
+ }
+ dlopens.push_back(path);
+ }
+ else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
+ verboseFixups = true;
+ }
+ else if ( strcmp(arg, "-build_root") == 0 ) {
+ const char* buildRootPath = argv[++i];
+ if ( buildRootPath == nullptr ) {
+ fprintf(stderr, "-build_root option requires a path \n");
+ return 1;
+ }
+ buildtimePrefixes.push_back(buildRootPath);
+ }
+ else if ( strcmp(arg, "-o") == 0 ) {
+ outPath = argv[++i];
+ if ( outPath == nullptr ) {
+ fprintf(stderr, "-o option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-print_closure_file") == 0 ) {
+ printPath = argv[++i];
+ if ( printPath == nullptr ) {
+ fprintf(stderr, "-print_closure_file option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-print_image_group") == 0 ) {
+ printGroupPath = argv[++i];
+ if ( printGroupPath == nullptr ) {
+ fprintf(stderr, "-print_image_group option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) {
+ listCacheClosures = true;
+ }
+ else if ( strcmp(arg, "-list_dyld_cache_other_dylibs") == 0 ) {
+ listOtherDylibs = true;
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
+ printCacheClosure = argv[++i];
+ if ( printCacheClosure == nullptr ) {
+ fprintf(stderr, "-print_dyld_cache_closure option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_closures") == 0 ) {
+ printClosures = true;
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) {
+ printCachedDylibs = true;
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_other_dylibs") == 0 ) {
+ printOtherDylibs = true;
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) {
+ printCachedDylib = argv[++i];
+ if ( printCachedDylib == nullptr ) {
+ fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_other") == 0 ) {
+ printOtherDylib = argv[++i];
+ if ( printOtherDylib == nullptr ) {
+ fprintf(stderr, "-print_dyld_cache_other option requires a path \n");
+ return 1;
+ }
+ }
+ else if ( strcmp(arg, "-print_dyld_cache_patch_table") == 0 ) {
+ printPatchTable = true;
+ }
+ else if ( strcmp(arg, "-include_all_dylibs_in_dir") == 0 ) {
+ includeAllDylibs = true;
+ }
+ else if ( strcmp(arg, "-env") == 0 ) {
+ const char* envArg = argv[++i];
+ if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) {
+ fprintf(stderr, "-env option requires KEY=VALUE\n");
+ return 1;
+ }
+ envArgs.push_back(envArg);
+ }
+ else if ( strcmp(arg, "-use_closured") == 0 ) {
+ useClosured = true;
+ }
+ else {
+ fprintf(stderr, "unknown option %s\n", arg);
+ return 1;
+ }
+ }
+
+ if ( (inputMainExecutablePath || inputTopImagePath) && printPath ) {
+ fprintf(stderr, "-create_closure and -print_closure_file are mutually exclusive");
+ return 1;
+ }
+
+ const DyldSharedCache* dyldCache = nullptr;
+ bool dyldCacheIsRaw = false;
+ if ( cacheFilePath != nullptr ) {
+ dyldCache = mapCacheFile(cacheFilePath);
+ dyldCacheIsRaw = true;
+ }
+ else {
+#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+ size_t cacheLength;
+ dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
+ dyldCacheIsRaw = false;
+#endif
+ }
+ dyld3::ClosureBuffer::CacheIdent cacheIdent;
+ dyldCache->getUUID(cacheIdent.cacheUUID);
+ cacheIdent.cacheAddress = (unsigned long)dyldCache;
+ cacheIdent.cacheMappedSize = dyldCache->mappedSize();
+ dyld3::DyldCacheParser cacheParser(dyldCache, dyldCacheIsRaw);
+
+ if ( buildtimePrefixes.empty() )
+ buildtimePrefixes.push_back("");
+
+ std::vector<const dyld3::launch_cache::binary_format::ImageGroup*> existingGroups;
+ const dyld3::launch_cache::BinaryClosureData* mainClosure = nullptr;
+ if ( inputMainExecutablePath != nullptr ) {
+ dyld3::PathOverrides pathStuff(envArgs);
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3+dlopens.size(), theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> groupList(2, &theGroups[0]);
+ dyld3::ClosureBuffer clsBuffer(cacheIdent, inputMainExecutablePath, groupList, pathStuff);
+
+ std::string mainPath = inputMainExecutablePath;
+ for (const std::string& prefix : buildtimePrefixes) {
+ if ( startsWith(mainPath, prefix) ) {
+ mainPath = mainPath.substr(prefix.size());
+ if ( mainPath[0] != '/' )
+ mainPath = "/" + mainPath;
+ break;
+ }
+ }
+
+ Diagnostics closureDiag;
+ //if ( useClosured )
+ // mainClosure = closured_makeClosure(closureDiag, clsBuffer);
+ // else
+ mainClosure = dyld3::ImageProxyGroup::makeClosure(closureDiag, clsBuffer, mach_task_self(), buildtimePrefixes);
+ if ( closureDiag.hasError() ) {
+ fprintf(stderr, "dyld_closure_util: %s\n", closureDiag.errorMessage().c_str());
+ return 1;
+ }
+ for (const std::string& warn : closureDiag.warnings() )
+ fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+
+ dyld3::launch_cache::Closure closure(mainClosure);
+ if ( outPath != nullptr ) {
+ safeSave(mainClosure, closure.size(), outPath);
+ }
+ else {
+ dyld3::launch_cache::Closure theClosure(mainClosure);
+ theGroups[2] = theClosure.group().binaryData();
+ if ( !dlopens.empty() )
+ printf("[\n");
+ closure.printAsJSON(dyld3::launch_cache::ImageGroupList(3, &theGroups[0]), true);
+
+ int groupIndex = 3;
+ for (const char* path : dlopens) {
+ printf(",\n");
+ dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> groupList2(groupIndex-2, &theGroups[2]);
+ dyld3::ClosureBuffer dlopenBuffer(cacheIdent, path, groupList2, pathStuff);
+ Diagnostics dlopenDiag;
+ //if ( useClosured )
+ // theGroups[groupIndex] = closured_makeDlopenGroup(closureDiag, clsBuffer);
+ //else
+ theGroups[groupIndex] = dyld3::ImageProxyGroup::makeDlopenGroup(dlopenDiag, dlopenBuffer, mach_task_self(), buildtimePrefixes);
+ if ( dlopenDiag.hasError() ) {
+ fprintf(stderr, "dyld_closure_util: %s\n", dlopenDiag.errorMessage().c_str());
+ return 1;
+ }
+ for (const std::string& warn : dlopenDiag.warnings() )
+ fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+ dyld3::launch_cache::ImageGroup dlopenGroup(theGroups[groupIndex]);
+ dlopenGroup.printAsJSON(dyld3::launch_cache::ImageGroupList(groupIndex+1, &theGroups[0]), true);
+ ++groupIndex;
+ }
+ if ( !dlopens.empty() )
+ printf("]\n");
+ }
+
+ }
+#if 0
+ else if ( inputTopImagePath != nullptr ) {
+ std::string imagePath = inputTopImagePath;
+ for (const std::string& prefix : buildtimePrefixes) {
+ if ( startsWith(imagePath, prefix) ) {
+ imagePath = imagePath.substr(prefix.size());
+ if ( imagePath[0] != '/' )
+ imagePath = "/" + imagePath;
+ break;
+ }
+ }
+
+ Diagnostics igDiag;
+ existingGroups.push_back(dyldCache->cachedDylibsGroup());
+ existingGroups.push_back(dyldCache->otherDylibsGroup());
+ if ( existingClosuresPath != nullptr ) {
+ size_t mappedSize;
+ const void* imageGroups = mapFileReadOnly(existingClosuresPath, mappedSize);
+ if ( imageGroups == nullptr ) {
+ fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+ return 1;
+ }
+ uint32_t sentGroups = *(uint32_t*)imageGroups;
+ uint16_t lastGroupNum = 2;
+ existingGroups.resize(sentGroups+2);
+ const uint8_t* p = (uint8_t*)(imageGroups)+4;
+ //const uint8_t* end = (uint8_t*)(imageGroups) + mappedSize;
+ for (uint32_t i=0; i < sentGroups; ++i) {
+ const dyld3::launch_cache::binary_format::ImageGroup* aGroup = (const dyld3::launch_cache::binary_format::ImageGroup*)p;
+ existingGroups[2+i] = aGroup;
+ dyld3::launch_cache::ImageGroup imgrp(aGroup);
+ lastGroupNum = imgrp.groupNum();
+ p += imgrp.size();
+ }
+ }
+ const dyld3::launch_cache::binary_format::ImageGroup* ig = dyld3::ImageProxyGroup::makeDlopenGroup(igDiag, dyldCache, existingGroups.size(), existingGroups, imagePath, envArgs);
+ if ( igDiag.hasError() ) {
+ fprintf(stderr, "dyld_closure_util: %s\n", igDiag.errorMessage().c_str());
+ return 1;
+ }
+
+ dyld3::launch_cache::ImageGroup group(ig);
+ group.printAsJSON(dyldCache, true);
+ }
+#endif
+ else if ( printPath != nullptr ) {
+ size_t mappedSize;
+ const void* buff = mapFileReadOnly(printPath, mappedSize);
+ if ( buff == nullptr ) {
+ fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+ return 1;
+ }
+ dyld3::launch_cache::Closure theClosure((dyld3::launch_cache::binary_format::Closure*)buff);
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ theGroups[2] = theClosure.group().binaryData();
+ theClosure.printAsJSON(theGroups, verboseFixups);
+ //closure.printStatistics();
+ munmap((void*)buff, mappedSize);
+ }
+ else if ( printGroupPath != nullptr ) {
+ size_t mappedSize;
+ const void* buff = mapFileReadOnly(printGroupPath, mappedSize);
+ if ( buff == nullptr ) {
+ fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+ return 1;
+ }
+ dyld3::launch_cache::ImageGroup group((dyld3::launch_cache::binary_format::ImageGroup*)buff);
+// group.printAsJSON(dyldCache, verboseFixups);
+ munmap((void*)buff, mappedSize);
+ }
+ else if ( listCacheClosures ) {
+ cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) {
+ dyld3::launch_cache::Closure closure(closureBinary);
+ printf("%6lu %s\n", closure.size(), runtimePath);
+ });
+ }
+ else if ( listOtherDylibs ) {
+ dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup());
+ for (uint32_t i=0; i < dylibGroup.imageCount(); ++i) {
+ dyld3::launch_cache::Image image = dylibGroup.image(i);
+ printf("%s\n", image.path());
+ }
+ }
+ else if ( printCacheClosure ) {
+ const dyld3::launch_cache::BinaryClosureData* cls = cacheParser.findClosure(printCacheClosure);
+ if ( cls != nullptr ) {
+ dyld3::launch_cache::Closure theClosure(cls);
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ theGroups[2] = theClosure.group().binaryData();
+ theClosure.printAsJSON(theGroups, verboseFixups);
+ }
+ else {
+ fprintf(stderr, "no closure in cache for %s\n", printCacheClosure);
+ }
+ }
+ else if ( printClosures ) {
+ cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) {
+ dyld3::launch_cache::Closure theClosure(closureBinary);
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ theGroups[2] = theClosure.group().binaryData();
+ theClosure.printAsJSON(theGroups, verboseFixups);
+ });
+ }
+ else if ( printCachedDylibs ) {
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ dyld3::launch_cache::ImageGroup dylibGroup(theGroups[0]);
+ dylibGroup.printAsJSON(theGroups, verboseFixups);
+ }
+ else if ( printCachedDylib != nullptr ) {
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup());
+ uint32_t imageIndex;
+ const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printCachedDylib, imageIndex);
+ if ( binImage != nullptr ) {
+ dyld3::launch_cache::Image image(binImage);
+ image.printAsJSON(theGroups, true);
+ }
+ else {
+ fprintf(stderr, "no such other image found\n");
+ }
+ }
+ else if ( printOtherDylibs ) {
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ dyld3::launch_cache::ImageGroup dylibGroup(theGroups[1]);
+ dylibGroup.printAsJSON(theGroups, verboseFixups);
+ }
+ else if ( printOtherDylib != nullptr ) {
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup());
+ uint32_t imageIndex;
+ const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printOtherDylib, imageIndex);
+ if ( binImage != nullptr ) {
+ dyld3::launch_cache::Image image(binImage);
+ image.printAsJSON(theGroups, true);
+ }
+ else {
+ fprintf(stderr, "no such other image found\n");
+ }
+ }
+ else if ( printPatchTable ) {
+ __block uint64_t cacheBaseAddress = 0;
+ dyldCache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+ if ( cacheBaseAddress == 0 )
+ cacheBaseAddress = vmAddr;
+ });
+ __block std::vector<CachedSections> sections;
+ __block bool hasError = false;
+ dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+ dyld3::MachOParser parser(mh, dyldCacheIsRaw);
+ parser.forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content,
+ uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+ if ( illegalSectionSize ) {
+ fprintf(stderr, "dyld_closure_util: section size extends beyond the end of the segment %s/%s\n", segName, sectionName);
+ stop = true;
+ return;
+ }
+ uint32_t offsetStart = (uint32_t)(addr - cacheBaseAddress);
+ uint32_t offsetEnd = (uint32_t)(offsetStart + size);
+ sections.push_back({offsetStart, offsetEnd, addr, mh, segName, sectionName, installName});
+ });
+ });
+ if (hasError)
+ return 1;
+ dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup());
+ dylibGroup.forEachDyldCachePatchLocation(cacheParser, ^(uint32_t targetCacheVmOffset, const std::vector<uint32_t>& usesPointersCacheVmOffsets, bool& stop) {
+ const CachedSections& targetSection = find(targetCacheVmOffset, sections);
+ dyld3::MachOParser targetParser(targetSection.mh, dyldCacheIsRaw);
+ const char* symbolName;
+ uint64_t symbolAddress;
+ if ( targetParser.findClosestSymbol(targetSection.vmAddress + targetCacheVmOffset - targetSection.mappedOffsetStart, &symbolName, &symbolAddress) ) {
+ printf("%s: [cache offset = 0x%08X]\n", symbolName, targetCacheVmOffset);
+ }
+ else {
+ printf("0x%08X from %40s %10s %16s + 0x%06X\n", targetCacheVmOffset, strrchr(targetSection.dylibPath, '/')+1, targetSection.segmentName.c_str(), targetSection.sectionName.c_str(), targetCacheVmOffset - targetSection.mappedOffsetStart);
+ }
+ for (uint32_t offset : usesPointersCacheVmOffsets) {
+ const CachedSections& usedInSection = find(offset, sections);
+ printf("%40s %10s %16s + 0x%06X\n", strrchr(usedInSection.dylibPath, '/')+1, usedInSection.segmentName.c_str(), usedInSection.sectionName.c_str(), offset - usedInSection.mappedOffsetStart);
+ }
+ });
+ }
+
+
+ return 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@
+ */
+
+#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 <unordered_set>
+#include <algorithm>
+
+#include <spawn.h>
+
+#include <Bom/Bom.h>
+
+#include "Manifest.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "BuilderUtils.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "MachOParser.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;
+
+static const char* tempRootDirTemplate = "/tmp/dyld_shared_cache_builder.XXXXXX";
+static char* tempRootDir = nullptr;
+
+int runCommandAndWait(Diagnostics& diags, const char* args[])
+{
+ pid_t pid;
+ int status;
+ int res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ);
+ if (res != 0)
+ diags.error("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;
+ }
+ }
+
+ return res;
+}
+
+void processRoots(Diagnostics& diags, std::set<std::string>& roots)
+{
+ std::set<std::string> processedRoots;
+ struct stat sb;
+ int res = 0;
+ const char* args[8];
+
+ for (const auto& root : roots) {
+ res = stat(root.c_str(), &sb);
+
+ if (res == 0 && S_ISDIR(sb.st_mode)) {
+ roots.insert(root);
+ return;
+ } else if (endsWith(root, ".cpio") || endsWith(root, ".cpio.gz") || endsWith(root, ".cpgz") || endsWith(root, ".cpio.bz2") || endsWith(root, ".cpbz2") || endsWith(root, ".pax") || endsWith(root, ".pax.gz") || endsWith(root, ".pgz") || endsWith(root, ".pax.bz2") || endsWith(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 (endsWith(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 (endsWith(root, ".tar.gz") || endsWith(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 (endsWith(root, ".tar.bz2")
+ || endsWith(root, ".tbz2")
+ || endsWith(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 (endsWith(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 (endsWith(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 {
+ diags.error("unknown archive type: %s", root.c_str());
+ continue;
+ }
+
+ if (res != runCommandAndWait(diags, args)) {
+ fprintf(stderr, "Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res);
+ exit(-1);
+ }
+ for (auto& existingRoot : processedRoots) {
+ if (existingRoot == tempRootDir)
+ return;
+ }
+
+ processedRoots.insert(tempRootDir);
+ }
+
+ roots = processedRoots;
+}
+
+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 {
+ __block Diagnostics diags;
+ std::set<std::string> roots;
+ std::string dylibCacheDir;
+ std::string release;
+ bool emitDevCaches = true;
+ bool emitElidedDylibs = true;
+ bool listConfigs = false;
+ bool copyRoots = false;
+ bool debug = false;
+ std::string dstRoot;
+ std::string configuration;
+ std::string resultPath;
+
+ 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) {
+ diags = Diagnostics(true);
+ debug = true;
+ } else if (strcmp(arg, "-list_configs") == 0) {
+ listConfigs = true;
+ } else if (strcmp(arg, "-root") == 0) {
+ roots.insert(realPath(argv[++i]));
+ } else if (strcmp(arg, "-copy_roots") == 0) {
+ copyRoots = true;
+ } else if (strcmp(arg, "-dylib_cache") == 0) {
+ dylibCacheDir = realPath(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 = realPath(argv[++i]);
+ } else if (strcmp(arg, "-release") == 0) {
+ release = argv[++i];
+ } else if (strcmp(arg, "-results") == 0) {
+ resultPath = realPath(argv[++i]);
+ } else {
+ //usage();
+ diags.error("unknown option: %s\n", arg);
+ }
+ } else {
+ if (!configuration.empty()) {
+ diags.error("You may only specify one configuration");
+ }
+ configuration = argv[i];
+ }
+ }
+
+ time_t mytime = time(0);
+ fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
+ processRoots(diags, roots);
+
+ struct rlimit rl = { OPEN_MAX, OPEN_MAX };
+ (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+ if (dylibCacheDir.empty() && release.empty()) {
+ fprintf(stderr, "you must specify either -dylib_cache or -release");
+ exit(-1);
+ } else if (!dylibCacheDir.empty() && !release.empty()) {
+ fprintf(stderr, "you may not use -dylib_cache and -release at the same time");
+ exit(-1);
+ }
+
+ if ((configuration.empty() || dstRoot.empty()) && !listConfigs) {
+ fprintf(stderr, "Must specify a configuration and a valid -dst_root OR -list_configs\n");
+ exit(-1);
+ }
+
+ 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());
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots);
+
+ if (manifest.build().empty()) {
+ fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
+ exit(-1);
+ }
+ fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
+
+ if (listConfigs) {
+ manifest.forEachConfiguration([](const std::string& configName) {
+ printf("%s\n", configName.c_str());
+ });
+ }
+
+ if (!manifest.filterForConfig(configuration)) {
+ fprintf(stderr, "No config %s. Please run with -list_configs to see configurations available for this %s.\n",
+ configuration.c_str(), manifest.build().c_str());
+ exit(-1);
+ }
+ manifest.calculateClosure();
+
+ std::vector<dyld3::BuildQueueEntry> buildQueue;
+
+ bool cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false);
+
+ if (!cacheBuildSuccess) {
+ exit(-1);
+ }
+
+ writeRootList(dstRoot, roots);
+
+ if (copyRoots) {
+ manifest.forEachConfiguration([&manifest](const std::string& configName) {
+ for (auto& arch : manifest.configuration(configName).architectures) {
+ for (auto& dylib : arch.second.results.dylibs) {
+ if (dylib.second.included) {
+ dyld3::MachOParser parser = manifest.parserForUUID(dylib.first);
+ cachePaths.insert(parser.installName());
+ }
+ }
+ }
+ });
+
+ BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
+ BOMCopierSetCopyFileStartedHandler(copier, filteredCopy);
+ for (auto& root : roots) {
+ BOMCopierCopy(copier, root.c_str(), dstRoot.c_str());
+ }
+ BOMCopierFree(copier);
+ }
+
+
+
+
+ int err = sync_volume_np(dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
+ if (err) {
+ fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
+ }
+
+ // 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.
+
+ if (!resultPath.empty()) {
+ manifest.write(resultPath);
+ }
+
+ const char* args[8];
+ args[0] = (char*)"/bin/rm";
+ args[1] = (char*)"-rf";
+ args[2] = (char*)tempRootDir;
+ args[3] = nullptr;
+ (void)runCommandAndWait(diags, args);
+
+ for (const std::string& warn : diags.warnings()) {
+ fprintf(stderr, "dyld_shared_cache_builder: warning: %s\n", warn.c_str());
+ }
+ exit(0);
+ });
+ }
+
+ dispatch_main();
+
+ return 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@
+ */
+
+#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 <assert.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>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+
+
+struct MappedMachOsByCategory
+{
+ std::string archName;
+ std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
+ std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
+ std::vector<DyldSharedCache::MappedMachO> mainExecutables;
+};
+
+static bool verbose = false;
+
+
+static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+ // read start of file to determine if it is mach-o or a fat file
+ std::string fullPath = buildRootPath + runtimePath;
+ int fd = ::open(fullPath.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return false;
+ bool result = false;
+ const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( wholeFile != MAP_FAILED ) {
+ Diagnostics diag;
+ bool usedWholeFile = false;
+ for (MappedMachOsByCategory& file : files) {
+ size_t sliceOffset;
+ size_t sliceLength;
+ bool fatButMissingSlice;
+ const void* slice = MAP_FAILED;
+ if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+ slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+ if ( slice != MAP_FAILED ) {
+ //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+ if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) {
+ ::munmap((void*)slice, sliceLength);
+ slice = MAP_FAILED;
+ }
+ }
+ }
+ else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+ slice = wholeFile;
+ sliceLength = statBuf.st_size;
+ sliceOffset = 0;
+ usedWholeFile = true;
+ //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+ }
+ if ( slice != MAP_FAILED ) {
+ const mach_header* mh = (mach_header*)slice;
+ dyld3::MachOParser parser(mh);
+ if ( parser.platform() != platform ) {
+ fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
+ result = false;
+ }
+ else {
+ bool sip = true; // assume anything found in the simulator runtime is a platform binary
+ if ( parser.isDynamicExecutable() ) {
+ bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ else {
+ if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+ file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ else {
+ file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ }
+ result = true;
+ }
+ }
+ }
+ if ( !usedWholeFile )
+ ::munmap((void*)wholeFile, statBuf.st_size);
+ }
+ ::close(fd);
+ return result;
+}
+
+
+static bool parsePathsFile(const std::string& filePath, std::vector<std::string>& paths) {
+ 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.push_back(line);
+ }
+ myfile.close();
+ return true;
+ }
+ return false;
+}
+
+
+static void mapAllFiles(const std::string& dylibsRootDir, const std::vector<std::string>& paths, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+ for (const std::string& runtimePath : paths) {
+ std::string fullPath = dylibsRootDir + runtimePath;
+ struct stat statBuf;
+ if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) )
+ fprintf(stderr, "could not load: %s\n", fullPath.c_str());
+ }
+}
+
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+ return (uint32_t)(abstime/1000/1000);
+}
+
+
+#define TERMINATE_IF_LAST_ARG( s ) \
+ do { \
+ if ( i == argc - 1 ) { \
+ fprintf(stderr, s ); \
+ return 1; \
+ } \
+ } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+ std::string rootPath;
+ std::string dylibListFile;
+ bool force = false;
+ std::string cacheDir;
+ std::string dylibsList;
+ std::unordered_set<std::string> archStrs;
+
+ dyld3::Platform platform = dyld3::Platform::iOS;
+
+ // parse command line options
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (strcmp(arg, "-debug") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-verbose") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-tvOS") == 0) {
+ platform = dyld3::Platform::tvOS;
+ }
+ else if (strcmp(arg, "-iOS") == 0) {
+ platform = dyld3::Platform::iOS;
+ }
+ else if (strcmp(arg, "-watchOS") == 0) {
+ platform = dyld3::Platform::watchOS;
+ }
+ else if ( strcmp(arg, "-root") == 0 ) {
+ TERMINATE_IF_LAST_ARG("-root missing path argument\n");
+ rootPath = argv[++i];
+ }
+ else if ( strcmp(arg, "-dylibs_list") == 0 ) {
+ TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n");
+ dylibsList = 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 {
+ //usage();
+ fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
+ return 1;
+ }
+ }
+
+ if ( cacheDir.empty() ) {
+ fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
+ return 1;
+ }
+
+ if ( rootPath.empty() ) {
+ fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
+ return 1;
+ }
+ else {
+ // canonicalize rootPath
+ char resolvedPath[PATH_MAX];
+ if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+ rootPath = resolvedPath;
+ }
+ if ( rootPath.back() != '/' )
+ rootPath = rootPath + "/";
+ }
+
+ int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ if ( (err != 0) && (err != EEXIST) ) {
+ fprintf(stderr, "mkpath_np fail: %d", err);
+ return 1;
+ }
+
+ if ( archStrs.empty() ) {
+ switch ( platform ) {
+ case dyld3::Platform::iOS:
+ case dyld3::Platform::tvOS:
+ archStrs.insert("arm64");
+ break;
+ case dyld3::Platform::watchOS:
+ archStrs.insert("armv7k");
+ break;
+ case dyld3::Platform::unknown:
+ case dyld3::Platform::macOS:
+ assert(0 && "macOS not support with this tool");
+ break;
+ }
+ }
+
+ uint64_t t1 = mach_absolute_time();
+
+ // find all mach-o files for requested architectures
+ std::vector<MappedMachOsByCategory> allFileSets;
+ if ( archStrs.count("arm64") )
+ allFileSets.push_back({"arm64"});
+ if ( archStrs.count("armv7k") )
+ allFileSets.push_back({"armv7k"});
+ std::vector<std::string> paths;
+ parsePathsFile(dylibsList, paths);
+ mapAllFiles(rootPath, paths, platform, allFileSets);
+
+ uint64_t t2 = mach_absolute_time();
+
+ fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+
+ // build all caches in parallel
+ __block bool cacheBuildFailure = false;
+ dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+ const MappedMachOsByCategory& fileSet = allFileSets[index];
+ const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+
+ fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+
+ // build cache new cache file
+ DyldSharedCache::CreateOptions options;
+ options.archName = fileSet.archName;
+ options.platform = platform;
+ options.excludeLocalSymbols = true;
+ options.optimizeStubs = false;
+ options.optimizeObjC = true;
+ options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
+ DyldSharedCache::Agile : DyldSharedCache::SHA256only;
+ options.dylibsRemovedDuringMastering = true;
+ options.inodesAreSameAsRuntime = false;
+ options.cacheSupportsASLR = true;
+ options.forSimulator = false;
+ options.verbose = verbose;
+ options.evictLeafDylibsOnOverflow = false;
+ options.pathPrefixes = { rootPath };
+ DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+ // print any warnings
+ for (const std::string& warn : results.warnings) {
+ fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+ }
+ if ( !results.errorMessage.empty() ) {
+ // print error (if one)
+ fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
+ cacheBuildFailure = true;
+ }
+ else {
+ // save new cache file to disk and write new .map file
+ assert(results.cacheContent != nullptr);
+ if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+ cacheBuildFailure = true;
+ if ( !cacheBuildFailure ) {
+ std::string mapStr = results.cacheContent->mapFile();
+ std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+ safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+ }
+ // free created cache buffer
+ vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+ }
+ });
+
+ // we could unmap all input files, but tool is about to quit
+
+ return (cacheBuildFailure ? 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@
+ */
+
+#include <Bom/Bom.h>
+#include <dispatch/dispatch.h>
+#include <copyfile.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 "Manifest.h"
+#include "FileUtils.h"
+#include "BuilderUtils.h"
+
+#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL
+
+#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.cache-builder.build", DISPATCH_QUEUE_CONCURRENT);
+
+#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/"
+
+void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables)
+{
+ auto copy_state = copyfile_state_alloc();
+ mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755);
+ (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+
+ if (!manifest.dylibOrderFile().empty()) {
+ (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+ }
+
+ if (!manifest.dirtyDataOrderFile().empty()) {
+ (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+ }
+
+ std::set<dyld3::UUID> uuids;
+ std::map<std::string, std::string> copy_pairs;
+
+ manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) {
+ manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) {
+ auto results = manifest.configuration(config).architecture(arch).results;
+ for (const auto& image : results.dylibs) {
+ uuids.insert(image.first);
+ }
+ for (const auto& image : results.bundles) {
+ uuids.insert(image.first);
+ }
+ for (const auto& image : results.executables) {
+ uuids.insert(image.first);
+ }
+ });
+ });
+
+ for (auto& uuid : uuids) {
+ auto buildPath = manifest.buildPathForUUID(uuid);
+ auto installPath = manifest.runtimePathForUUID(uuid);
+ assert(!buildPath.empty() && !installPath.empty());
+ copy_pairs.insert(std::make_pair(installPath, buildPath));
+ }
+
+ for (const auto& copy_pair : copy_pairs) {
+ std::string from = realPath(copy_pair.second);
+ std::string to = dylibCachePath + "/Root/" + copy_pair.first;
+ mkpath_np(dirPath(to).c_str(), 0755);
+ int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+ diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str());
+
+ }
+ copyfile_state_free(copy_state);
+
+ fprintf(stderr, "[Artifact] dylibs copied\n");
+}
+
+void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest)
+{
+ manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
+ manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
+ manifest.setMetabomFile("./Metadata/metabom.bom");
+
+ for (auto& projects : manifest.projects()) {
+ manifest.addProjectSource(projects.first, "./Root", true);
+ }
+}
+
+int main(int argc, const char* argv[])
+{
+ @autoreleasepool {
+ __block Diagnostics diags;
+ bool verbose = false;
+ std::string masterDstRoot;
+ std::string dylibCacheDir;
+ std::string resultPath;
+ std::string manifestPath;
+ bool preflight = false;
+ __block bool allBuildsSucceeded = true;
+ bool skipWrites = false;
+ bool skipBuilds = false;
+ bool agileChooseSHA256CdHash = false;
+ time_t mytime = time(0);
+ fprintf(stderr, "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) {
+ verbose = true;
+ diags = Diagnostics(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()) {
+ diags.error("-master_dst_root missing path argument");
+ }
+ } else if (strcmp(arg, "-results") == 0) {
+ resultPath = argv[++i];
+ } else if (strcmp(arg, "-plist") == 0) {
+ manifestPath = argv[++i];
+ } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) {
+ agileChooseSHA256CdHash = true;
+ } else {
+ // usage();
+ diags.error("unknown option: %s", arg);
+ }
+ } else {
+ manifestPath = argv[i];
+ }
+ }
+
+ if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) {
+ skipBuilds = true;
+ }
+
+ if (diags.hasError()) {
+ printf("%s\n", diags.errorMessage().c_str());
+ exit(-1);
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (manifestPath.empty()) {
+ fprintf(stderr, "mainfest path argument is required\n");
+ exit(-1);
+ }
+
+ (void)chdir(dirname(strdup(manifestPath.c_str())));
+ __block auto manifest = dyld3::Manifest(diags, manifestPath);
+
+ if (manifest.build().empty()) {
+ fprintf(stderr, "No version found in manifest\n");
+ exit(-1);
+ }
+
+ fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
+
+ if (masterDstRoot.empty()) {
+ fprintf(stderr, "-master_dst_root required path argument\n");
+ exit(-1);
+ }
+
+ if (manifest.version() < 4) {
+ fprintf(stderr, "must specify valid manifest file\n");
+ exit(-1);
+ }
+
+ struct rlimit rl = { OPEN_MAX, OPEN_MAX };
+ (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+ manifest.calculateClosure();
+
+ if (!skipWrites && !skipBuilds) {
+ (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
+ dispatch_group_async(buildGroup(), build_queue, ^{
+ createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true);
+ });
+ }
+
+ if (!dylibCacheDir.empty()) {
+ dispatch_group_async(buildGroup(), build_queue, ^{
+ createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false);
+ });
+ }
+
+ if (!skipBuilds) {
+ dispatch_group_async(buildGroup(), build_queue, ^{
+ makeBoms(manifest, masterDstRoot);
+ });
+ allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
+ agileChooseSHA256CdHash);
+ }
+
+ manifest.write(resultPath);
+
+ addArtifactPaths(diags, manifest);
+ if (!dylibCacheDir.empty()) {
+ manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist");
+ manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json");
+ }
+
+ if (!skipWrites) {
+ mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755);
+ auto copy_state = copyfile_state_alloc();
+ (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL);
+ copyfile_state_free(copy_state);
+ manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist");
+ manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json");
+ }
+
+ dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER);
+ time_t mytime = time(0);
+ fprintf(stderr, "Finished: %s", asctime(localtime(&mytime)));
+
+ if (preflight && !allBuildsSucceeded) {
+ exit(-1);
+ }
+
+ exit(0);
+ });
+ }
+ 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 <assert.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>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+#include <Bom/Bom.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+struct MappedMachOsByCategory
+{
+ std::string archName;
+ std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
+ std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
+ std::vector<DyldSharedCache::MappedMachO> mainExecutables;
+};
+
+static const char* sAllowedPrefixes[] = {
+ "/bin/",
+ "/sbin/",
+ "/usr/",
+ "/System",
+ "/Applications/App Store.app/",
+ "/Applications/Automator.app/",
+ "/Applications/Calculator.app/",
+ "/Applications/Calendar.app/",
+ "/Applications/Chess.app/",
+ "/Applications/Contacts.app/",
+// "/Applications/DVD Player.app/",
+ "/Applications/Dashboard.app/",
+ "/Applications/Dictionary.app/",
+ "/Applications/FaceTime.app/",
+ "/Applications/Font Book.app/",
+ "/Applications/Image Capture.app/",
+ "/Applications/Launchpad.app/",
+ "/Applications/Mail.app/",
+ "/Applications/Maps.app/",
+ "/Applications/Messages.app/",
+ "/Applications/Mission Control.app/",
+ "/Applications/Notes.app/",
+ "/Applications/Photo Booth.app/",
+// "/Applications/Photos.app/",
+ "/Applications/Preview.app/",
+ "/Applications/QuickTime Player.app/",
+ "/Applications/Reminders.app/",
+ "/Applications/Safari.app/",
+ "/Applications/Siri.app/",
+ "/Applications/Stickies.app/",
+ "/Applications/System Preferences.app/",
+ "/Applications/TextEdit.app/",
+ "/Applications/Time Machine.app/",
+ "/Applications/iBooks.app/",
+ "/Applications/iTunes.app/",
+ "/Applications/Utilities/Activity Monitor.app",
+ "/Applications/Utilities/AirPort Utility.app",
+ "/Applications/Utilities/Audio MIDI Setup.app",
+ "/Applications/Utilities/Bluetooth File Exchange.app",
+ "/Applications/Utilities/Boot Camp Assistant.app",
+ "/Applications/Utilities/ColorSync Utility.app",
+ "/Applications/Utilities/Console.app",
+ "/Applications/Utilities/Digital Color Meter.app",
+ "/Applications/Utilities/Disk Utility.app",
+ "/Applications/Utilities/Grab.app",
+ "/Applications/Utilities/Grapher.app",
+ "/Applications/Utilities/Keychain Access.app",
+ "/Applications/Utilities/Migration Assistant.app",
+ "/Applications/Utilities/Script Editor.app",
+ "/Applications/Utilities/System Information.app",
+ "/Applications/Utilities/Terminal.app",
+ "/Applications/Utilities/VoiceOver Utility.app",
+ "/Library/CoreMediaIO/Plug-Ins/DAL/" // temp until plugins moved or closured working
+};
+
+static const char* sDontUsePrefixes[] = {
+ "/usr/share",
+ "/usr/local/",
+ "/System/Library/Assets",
+ "/System/Library/StagedFrameworks",
+ "/System/Library/Kernels/",
+ "/bin/zsh", // until <rdar://31026756> is fixed
+ "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker", // these load third party plugins
+ "/usr/bin/mdimport", // these load third party plugins
+};
+
+
+static bool verbose = false;
+
+
+
+static bool addIfMachO(const std::string& pathPrefix, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+ // don't precompute closure info for any debug or profile dylibs
+ if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") )
+ return false;
+
+ // read start of file to determine if it is mach-o or a fat file
+ std::string fullPath = pathPrefix + runtimePath;
+ int fd = ::open(fullPath.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return false;
+ bool result = false;
+ const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( wholeFile != MAP_FAILED ) {
+ Diagnostics diag;
+ bool usedWholeFile = false;
+ for (MappedMachOsByCategory& file : files) {
+ size_t sliceOffset;
+ size_t sliceLength;
+ bool fatButMissingSlice;
+ const void* slice = MAP_FAILED;
+ if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+ slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE | MAP_RESILIENT_CODESIGN, fd, sliceOffset);
+ if ( slice != MAP_FAILED ) {
+ //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+ if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, slice, sliceLength, fullPath.c_str(), false) ) {
+ ::munmap((void*)slice, sliceLength);
+ slice = MAP_FAILED;
+ }
+ }
+ }
+ else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+ slice = wholeFile;
+ sliceLength = statBuf.st_size;
+ sliceOffset = 0;
+ usedWholeFile = true;
+ //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+ }
+ std::vector<std::string> nonArchWarnings;
+ for (const std::string& warning : diag.warnings()) {
+ if ( !contains(warning, "required architecture") && !contains(warning, "not a dylib") )
+ nonArchWarnings.push_back(warning);
+ }
+ diag.clearWarnings();
+ if ( !nonArchWarnings.empty() ) {
+ fprintf(stderr, "update_dyld_shared_cache: warning: %s for %s: ", file.archName.c_str(), runtimePath.c_str());
+ for (const std::string& warning : nonArchWarnings) {
+ fprintf(stderr, "%s ", warning.c_str());
+ }
+ fprintf(stderr, "\n");
+ }
+ if ( slice != MAP_FAILED ) {
+ const mach_header* mh = (mach_header*)slice;
+ dyld3::MachOParser parser((mach_header*)slice);
+ bool sipProtected = isProtectedBySIP(fd);
+ bool issetuid = false;
+ if ( parser.isDynamicExecutable() ) {
+ // When SIP enabled, only build closures for SIP protected programs
+ if ( !requireSIP || sipProtected ) {
+ //fprintf(stderr, "requireSIP=%d, sipProtected=%d, path=%s\n", requireSIP, sipProtected, fullPath.c_str());
+ issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ }
+ else if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+ // when SIP is enabled, only dylib protected by SIP can go in cache
+ if ( !requireSIP || sipProtected )
+ file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ else
+ file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ else {
+ if ( parser.fileType() == MH_DYLIB ) {
+ std::string installName = parser.installName();
+ if ( startsWith(installName, "@") && !contains(runtimePath, ".app/") ) {
+ if ( startsWith(runtimePath, "/usr/lib/") || startsWith(runtimePath, "/System/Library/") )
+ fprintf(stderr, "update_dyld_shared_cache: warning @rpath install name for system framework: %s\n", runtimePath.c_str());
+ }
+ }
+ file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ result = true;
+ }
+ }
+ if ( !usedWholeFile )
+ ::munmap((void*)wholeFile, statBuf.st_size);
+ }
+ ::close(fd);
+ return result;
+}
+
+static void findAllFiles(const std::vector<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+ std::unordered_set<std::string> skipDirs;
+ for (const char* s : sDontUsePrefixes)
+ skipDirs.insert(s);
+
+ __block std::unordered_set<std::string> alreadyUsed;
+ bool multiplePrefixes = (pathPrefixes.size() > 1);
+ for (const std::string& prefix : pathPrefixes) {
+ // get all files from overlay for this search dir
+ for (const char* searchDir : sAllowedPrefixes ) {
+ iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
+ // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+ const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+ if ( !hasXBit && !endsWith(path, ".dylib") )
+ return;
+
+ // ignore files too small
+ if ( statBuf.st_size < 0x3000 )
+ return;
+
+ // don't add paths already found using previous prefix
+ if ( multiplePrefixes && (alreadyUsed.count(path) != 0) )
+ return;
+
+ // if the file is mach-o, add to list
+ if ( addIfMachO(prefix, path, statBuf, requireSIP, files) ) {
+ if ( multiplePrefixes )
+ alreadyUsed.insert(path);
+ }
+ });
+ }
+ }
+}
+
+
+static void findOSFilesViaBOMS(const std::vector<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+ __block std::unordered_set<std::string> runtimePathsFound;
+ for (const std::string& prefix : pathPrefixes) {
+ iterateDirectoryTree(prefix, "/System/Library/Receipts", ^(const std::string&) { return false; }, ^(const std::string& path, const struct stat& statBuf) {
+ if ( !contains(path, "com.apple.pkg.") )
+ return;
+ if ( !endsWith(path, ".bom") )
+ return;
+ std::string fullPath = prefix + path;
+ BOMBom bom = BOMBomOpenWithSys(fullPath.c_str(), false, NULL);
+ if ( bom == nullptr )
+ return;
+ BOMFSObject rootFso = BOMBomGetRootFSObject(bom);
+ if ( rootFso == nullptr ) {
+ BOMBomFree(bom);
+ return;
+ }
+ BOMBomEnumerator e = BOMBomEnumeratorNew(bom, rootFso);
+ if ( e == nullptr ) {
+ fprintf(stderr, "Can't get enumerator for BOM root FSObject\n");
+ return;
+ }
+ BOMFSObjectFree(rootFso);
+ //fprintf(stderr, "using BOM %s\n", path.c_str());
+ while (BOMFSObject fso = BOMBomEnumeratorNext(e)) {
+ if ( BOMFSObjectIsBinaryObject(fso) ) {
+ const char* runPath = BOMFSObjectPathName(fso);
+ if ( (runPath[0] == '.') && (runPath[1] == '/') )
+ ++runPath;
+ if ( runtimePathsFound.count(runPath) == 0 ) {
+ // only add files from sAllowedPrefixes and not in sDontUsePrefixes
+ bool inSearchDir = false;
+ for (const char* searchDir : sAllowedPrefixes ) {
+ if ( strncmp(searchDir, runPath, strlen(searchDir)) == 0 ) {
+ inSearchDir = true;
+ break;
+ }
+ }
+ if ( inSearchDir ) {
+ bool inSkipDir = false;
+ for (const char* skipDir : sDontUsePrefixes) {
+ if ( strncmp(skipDir, runPath, strlen(skipDir)) == 0 ) {
+ inSkipDir = true;
+ break;
+ }
+ }
+ if ( !inSkipDir ) {
+ for (const std::string& prefix2 : pathPrefixes) {
+ struct stat statBuf2;
+ std::string fullPath2 = prefix2 + runPath;
+ if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) {
+ addIfMachO(prefix2, runPath, statBuf2, requireSIP, files);
+ runtimePathsFound.insert(runPath);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ BOMFSObjectFree(fso);
+ }
+
+ BOMBomEnumeratorFree(e);
+ BOMBomFree(bom);
+ });
+ }
+}
+
+
+static bool dontCache(const std::string& volumePrefix, const std::string& archName,
+ const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
+ const DyldSharedCache::MappedMachO& aFile, bool warn,
+ const std::unordered_set<std::string>& skipDylibs)
+{
+ if ( skipDylibs.count(aFile.runtimePath) )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/QuickTime/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/Tcl/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/Perl/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/MonitorPanels/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/Accessibility/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/usr/local/") )
+ return true;
+
+ // anything inside a .app bundle is specific to app, so should not be in shared cache
+ if ( aFile.runtimePath.find(".app/") != std::string::npos )
+ return true;
+
+ if ( archName == "i386" ) {
+ if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") )
+ return true;
+ }
+
+ if ( aFile.runtimePath.find("//") != std::string::npos ) {
+ if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ return true;
+ }
+
+ dyld3::MachOParser parser(aFile.mh);
+ const char* installName = parser.installName();
+ if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
+ if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ return true;
+ }
+
+ if ( aFile.runtimePath != installName ) {
+ // see if install name is a symlink to actual path
+ std::string fullInstall = volumePrefix + installName;
+ char resolvedPath[PATH_MAX];
+ if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
+ std::string resolvedSymlink = resolvedPath;
+ if ( !volumePrefix.empty() ) {
+ resolvedSymlink = resolvedSymlink.substr(volumePrefix.size());
+ }
+ if ( aFile.runtimePath == resolvedSymlink ) {
+ return false;
+ }
+ }
+ if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ return true;
+ }
+ return false;
+}
+
+static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set<std::string>& skipDylibs, MappedMachOsByCategory& fileSet)
+{
+ std::unordered_set<std::string> pathsWithDuplicateInstallName;
+
+ std::unordered_map<std::string, std::string> installNameToFirstPath;
+ for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+ dyld3::MachOParser parser(aFile.mh);
+ const char* installName = parser.installName();
+ auto pos = installNameToFirstPath.find(installName);
+ if ( pos == installNameToFirstPath.end() ) {
+ installNameToFirstPath[installName] = aFile.runtimePath;
+ }
+ else {
+ pathsWithDuplicateInstallName.insert(aFile.runtimePath);
+ pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
+ }
+ }
+
+ for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+ if ( dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, true, skipDylibs) )
+ fileSet.otherDylibsAndBundles.push_back(aFile);
+ }
+ fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
+ [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, false, skipDylibs); }),
+ fileSet.dylibsForCache.end());
+}
+
+static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
+{
+ // other OS dylibs should not contain dylibs that are embedded in some .app bundle
+ fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(),
+ [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }),
+ fileSet.otherDylibsAndBundles.end());
+}
+
+
+static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
+{
+ // don't build closures for xcode shims in /usr/bin (e.g. /usr/bin/clang) which re-exec themselves to a tool inside Xcode.app
+ fileSet.mainExecutables.erase(std::remove_if(fileSet.mainExecutables.begin(), fileSet.mainExecutables.end(),
+ [&](const DyldSharedCache::MappedMachO& aFile) {
+ if ( !startsWith(aFile.runtimePath, "/usr/bin/") )
+ return false;
+ dyld3::MachOParser parser(aFile.mh);
+ __block bool isXcodeShim = false;
+ parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool &stop) {
+ if ( strcmp(loadPath, "/usr/lib/libxcselect.dylib") == 0 )
+ isXcodeShim = true;
+ });
+ return isXcodeShim;
+ }), fileSet.mainExecutables.end());
+}
+
+static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
+{
+ // if no existing cache, it is not up-to-date
+ int fd = ::open(existingCache.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return false;
+
+ // build map of found dylibs
+ std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
+ for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+ //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
+ currentDylibMap[aFile.runtimePath] = &aFile;
+ }
+
+ // make sure all dylibs in existing cache have same mtime and inode as found dylib
+ __block bool foundMismatch = false;
+ const uint64_t cacheMapLen = 0x40000000;
+ void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( p != MAP_FAILED ) {
+ const DyldSharedCache* cache = (DyldSharedCache*)p;
+ cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
+ bool foundMatch = false;
+ auto pos = currentDylibMap.find(installName);
+ if ( pos != currentDylibMap.end() ) {
+ const DyldSharedCache::MappedMachO* foundDylib = pos->second;
+ if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
+ foundMatch = true;
+ }
+ }
+ if ( !foundMatch ) {
+ // use slow path and look for any dylib with a matching inode and mtime
+ bool foundSlow = false;
+ for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+ if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
+ foundSlow = true;
+ break;
+ }
+ }
+ if ( !foundSlow ) {
+ foundMismatch = true;
+ if ( verbose )
+ fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
+ }
+ }
+ });
+ ::munmap(p, cacheMapLen);
+ }
+
+ ::close(fd);
+
+ return !foundMismatch;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+ return (uint32_t)(abstime/1000/1000);
+}
+
+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 ) { \
+ fprintf(stderr, s ); \
+ return 1; \
+ } \
+ } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+ std::string rootPath;
+ std::string overlayPath;
+ std::string dylibListFile;
+ bool universal = false;
+ bool force = false;
+ bool searchDisk = false;
+ bool dylibsRemoved = false;
+ std::string cacheDir;
+ std::unordered_set<std::string> archStrs;
+ std::unordered_set<std::string> skipDylibs;
+
+ // parse command line options
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (strcmp(arg, "-debug") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-verbose") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-dont_map_local_symbols") == 0) {
+ //We are going to ignore this
+ }
+ 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, "-search_disk") == 0) {
+ searchDisk = true;
+ }
+ else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) {
+ dylibsRemoved = true;
+ }
+ 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 if (strcmp(arg, "-skip") == 0) {
+ TERMINATE_IF_LAST_ARG("-skip missing argument\n");
+ skipDylibs.insert(argv[++i]);
+ }
+ else {
+ //usage();
+ fprintf(stderr, "update_dyld_shared_cache: unknown option: %s\n", arg);
+ return 1;
+ }
+ }
+
+ if ( !rootPath.empty() & !overlayPath.empty() ) {
+ fprintf(stderr, "-root and -overlay cannot be used together\n");
+ return 1;
+ }
+ // canonicalize rootPath
+ if ( !rootPath.empty() ) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+ rootPath = resolvedPath;
+ }
+ // <rdar://problem/33223984> when building closures for boot volume, pathPrefixes should be empty
+ if ( rootPath == "/" ) {
+ rootPath = "";
+ }
+ }
+ // canonicalize overlayPath
+ if ( !overlayPath.empty() ) {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(overlayPath.c_str(), resolvedPath) != NULL ) {
+ overlayPath = resolvedPath;
+ }
+ }
+ //
+ // pathPrefixes for three modes:
+ // 1) no options: { "" } // search only boot volume
+ // 2) -overlay: { overlay, "" } // search overlay, then boot volume
+ // 3) -root: { root } // search only -root volume
+ //
+ std::vector<std::string> pathPrefixes;
+ if ( !overlayPath.empty() )
+ pathPrefixes.push_back(overlayPath);
+ pathPrefixes.push_back(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;
+ }
+
+ int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ if ( (err != 0) && (err != EEXIST) ) {
+ fprintf(stderr, "mkpath_np fail: %d", err);
+ return 1;
+ }
+
+ if ( archStrs.empty() ) {
+ 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");
+ }
+ }
+
+ uint64_t t1 = mach_absolute_time();
+
+ // find all mach-o files for requested architectures
+ bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir);
+ __block std::vector<MappedMachOsByCategory> allFileSets;
+ if ( archStrs.count("x86_64") )
+ allFileSets.push_back({"x86_64"});
+ if ( archStrs.count("x86_64h") )
+ allFileSets.push_back({"x86_64h"});
+ if ( archStrs.count("i386") )
+ allFileSets.push_back({"i386"});
+ if ( searchDisk )
+ findAllFiles(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets);
+ else {
+ std::unordered_set<std::string> runtimePathsFound;
+ findOSFilesViaBOMS(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets);
+ }
+
+ // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache
+ for (MappedMachOsByCategory& fileSet : allFileSets) {
+ pruneCachedDylibs(rootPath, skipDylibs, fileSet);
+ pruneOtherDylibs(rootPath, fileSet);
+ pruneExecutables(rootPath, fileSet);
+ }
+
+ uint64_t t2 = mach_absolute_time();
+ if ( verbose ) {
+ if ( searchDisk )
+ fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+ else
+ fprintf(stderr, "time to read BOM and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+ }
+
+ // build caches in parallel on machines with at leat 4GB of RAM
+ uint64_t memSize = 0;
+ size_t sz = sizeof(memSize);;
+ bool buildInParallel = false;
+ if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
+ if ( memSize >= 0x100000000ULL )
+ buildInParallel = true;
+ }
+ dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
+ : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL);
+
+ // build all caches
+ __block bool cacheBuildFailure = false;
+ __block bool wroteSomeCacheFile = false;
+ dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) {
+ MappedMachOsByCategory& fileSet = allFileSets[index];
+ const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+
+ DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
+ if ( skipDylibs.count(runtimePath) )
+ return DyldSharedCache::MappedMachO();
+ for (const std::string& prefix : pathPrefixes) {
+ std::string fullPath = prefix + runtimePath;
+ struct stat statBuf;
+ if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+ std::vector<MappedMachOsByCategory> mappedFiles;
+ mappedFiles.push_back({fileSet.archName});
+ if ( addIfMachO(prefix, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
+ if ( !mappedFiles.back().dylibsForCache.empty() )
+ return mappedFiles.back().dylibsForCache.back();
+ }
+ }
+ }
+ return DyldSharedCache::MappedMachO();
+ };
+ size_t startCount = fileSet.dylibsForCache.size();
+ std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
+ DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes);
+ for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
+ fprintf(stderr, "update_dyld_shared_cache: warning: %s not in .bom, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str());
+ }
+ for (auto& exclude : excludes) {
+ std::string reasons = "(\"";
+ for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
+ reasons += *i;
+ if (i != --exclude.second.end()) {
+ reasons += "\", \"";
+ }
+ }
+ reasons += "\")";
+ fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str());
+ fileSet.otherDylibsAndBundles.push_back(exclude.first);
+ }
+
+ // check if cache is already up to date
+ if ( !force ) {
+ if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
+ return;
+ }
+
+ // add any extra dylibs needed which were not in .bom
+ fprintf(stderr, "update_dyld_shared_cache: %s incorporating %lu OS dylibs, tracking %lu others, building closures for %lu executables\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+ //for (const DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+ // fprintf(stderr, " %s\n", aFile.runtimePath.c_str());
+ //}
+
+
+ // build cache new cache file
+ DyldSharedCache::CreateOptions options;
+ options.archName = fileSet.archName;
+ options.platform = dyld3::Platform::macOS;
+ options.excludeLocalSymbols = false;
+ options.optimizeStubs = false;
+ options.optimizeObjC = true;
+ options.codeSigningDigestMode = DyldSharedCache::SHA256only;
+ options.dylibsRemovedDuringMastering = dylibsRemoved;
+ options.inodesAreSameAsRuntime = true;
+ options.cacheSupportsASLR = (fileSet.archName != "i386");
+ options.forSimulator = false;
+ options.verbose = verbose;
+ options.evictLeafDylibsOnOverflow = true;
+ options.pathPrefixes = pathPrefixes;
+ DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+ // print any warnings
+ for (const std::string& warn : results.warnings) {
+ fprintf(stderr, "update_dyld_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+ }
+ if ( !results.errorMessage.empty() ) {
+ // print error (if one)
+ fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str());
+ cacheBuildFailure = true;
+ }
+ else {
+ // save new cache file to disk and write new .map file
+ assert(results.cacheContent != nullptr);
+ if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+ cacheBuildFailure = true;
+ if ( !cacheBuildFailure ) {
+ std::string mapStr = results.cacheContent->mapFile();
+ std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+ safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+ wroteSomeCacheFile = true;
+ }
+ // free created cache buffer
+ vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+ }
+ });
+
+
+ // Save off spintrace data
+ if ( wroteSomeCacheFile ) {
+ void* h = dlopen("/usr/lib/libdscsym.dylib", 0);
+ if ( h != nullptr ) {
+ typedef int (*dscym_func)(const char*);
+ dscym_func func = (dscym_func)dlsym(h, "dscsym_save_dscsyms_for_current_caches");
+ std::string nuggetRoot = rootPath;
+ if ( nuggetRoot.empty() )
+ nuggetRoot = overlayPath;
+ if ( nuggetRoot.empty() )
+ nuggetRoot = "/";
+ (*func)(nuggetRoot.c_str());
+ }
+ }
+
+
+ // we could unmap all input files, but tool is about to quit
+
+ return (cacheBuildFailure ? 1 : 0);
+}
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>com.apple.rootless.storage.dyld</key>
+ <true/>
+ </dict>
+</plist>
--- /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 <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 <assert.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>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+
+
+struct MappedMachOsByCategory
+{
+ std::string archName;
+ std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
+ std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
+ std::vector<DyldSharedCache::MappedMachO> mainExecutables;
+};
+
+static const char* sSearchDirs[] = {
+ "/bin",
+ "/sbin",
+ "/usr",
+ "/System",
+};
+
+static const char* sSkipDirs[] = {
+ "/usr/share",
+ "/usr/local/include",
+};
+
+
+static const char* sMacOsAdditions[] = {
+ "/usr/lib/system/libsystem_kernel.dylib",
+ "/usr/lib/system/libsystem_platform.dylib",
+ "/usr/lib/system/libsystem_pthread.dylib",
+};
+
+
+static bool verbose = false;
+
+static bool addIfMachO(const std::string& simRuntimeRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+ // read start of file to determine if it is mach-o or a fat file
+ std::string fullPath = simRuntimeRootPath + runtimePath;
+ int fd = ::open(fullPath.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return false;
+ bool result = false;
+ const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( wholeFile != MAP_FAILED ) {
+ Diagnostics diag;
+ bool usedWholeFile = false;
+ for (MappedMachOsByCategory& file : files) {
+ size_t sliceOffset;
+ size_t sliceLength;
+ bool fatButMissingSlice;
+ const void* slice = MAP_FAILED;
+ if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+ slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+ if ( slice != MAP_FAILED ) {
+ //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+ if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) {
+ ::munmap((void*)slice, sliceLength);
+ slice = MAP_FAILED;
+ }
+ }
+ }
+ else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+ slice = wholeFile;
+ sliceLength = statBuf.st_size;
+ sliceOffset = 0;
+ usedWholeFile = true;
+ //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+ }
+ if ( slice != MAP_FAILED ) {
+ const mach_header* mh = (mach_header*)slice;
+ dyld3::MachOParser parser(mh);
+ if ( parser.platform() != platform ) {
+ fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
+ result = false;
+ }
+ else {
+ bool sip = true; // assume anything found in the simulator runtime is a platform binary
+ if ( parser.isDynamicExecutable() ) {
+ bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ else {
+ if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+ file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ else {
+ file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ }
+ }
+ result = true;
+ }
+ }
+ }
+ if ( !usedWholeFile )
+ ::munmap((void*)wholeFile, statBuf.st_size);
+ }
+ ::close(fd);
+ return result;
+}
+
+static void findAllFiles(const std::string& simRuntimeRootPath, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+ std::unordered_set<std::string> skipDirs;
+ for (const char* s : sSkipDirs)
+ skipDirs.insert(s);
+
+ for (const char* searchDir : sSearchDirs ) {
+ iterateDirectoryTree(simRuntimeRootPath, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
+ // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+ const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+ if ( !hasXBit && !endsWith(path, ".dylib") )
+ return;
+
+ // ignore files too small
+ if ( statBuf.st_size < 0x3000 )
+ return;
+
+ // if the file is mach-o add to list
+ addIfMachO(simRuntimeRootPath, path, statBuf, platform, files);
+ });
+ }
+}
+
+static void addMacOSAdditions(std::vector<MappedMachOsByCategory>& allFileSets)
+{
+ for (const char* addPath : sMacOsAdditions) {
+ struct stat statBuf;
+ if ( stat(addPath, &statBuf) == 0 )
+ addIfMachO("", addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+ }
+}
+
+
+static bool dontCache(const std::string& simRuntimeRootPath, const std::string& archName,
+ const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
+ const DyldSharedCache::MappedMachO& aFile, bool warn)
+{
+ if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
+ return true;
+ if ( startsWith(aFile.runtimePath, "/usr/local/") )
+ return true;
+
+ // anything inside a .app bundle is specific to app, so should be in shared cache
+ if ( aFile.runtimePath.find(".app/") != std::string::npos )
+ return true;
+
+ if ( aFile.runtimePath.find("//") != std::string::npos ) {
+ if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s double-slash in install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ }
+
+ dyld3::MachOParser parser(aFile.mh);
+ const char* installName = parser.installName();
+ if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
+ if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ return true;
+ }
+
+ if ( aFile.runtimePath != installName ) {
+ // see if install name is a symlink to actual path
+ std::string fullInstall = simRuntimeRootPath + installName;
+ char resolvedPath[PATH_MAX];
+ if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
+ std::string resolvedSymlink = resolvedPath;
+ if ( !simRuntimeRootPath.empty() ) {
+ resolvedSymlink = resolvedSymlink.substr(simRuntimeRootPath.size());
+ }
+ if ( aFile.runtimePath == resolvedSymlink ) {
+ return false;
+ }
+ }
+ if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+ return true;
+ }
+
+ return false;
+}
+
+static void pruneCachedDylibs(const std::string& simRuntimeRootPath, MappedMachOsByCategory& fileSet)
+{
+ std::unordered_set<std::string> pathsWithDuplicateInstallName;
+
+ std::unordered_map<std::string, std::string> installNameToFirstPath;
+ for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+ //fprintf(stderr, "dylib: %s\n", aFile.runtimePath.c_str());
+ dyld3::MachOParser parser(aFile.mh);
+ const char* installName = parser.installName();
+ auto pos = installNameToFirstPath.find(installName);
+ if ( pos == installNameToFirstPath.end() ) {
+ installNameToFirstPath[installName] = aFile.runtimePath;
+ }
+ else {
+ pathsWithDuplicateInstallName.insert(aFile.runtimePath);
+ pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
+ }
+ }
+
+ for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+ if ( dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, true) )
+ fileSet.otherDylibsAndBundles.push_back(aFile);
+ }
+ fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
+ [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, false); }),
+ fileSet.dylibsForCache.end());
+}
+
+static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
+{
+ // if no existing cache, it is not up-to-date
+ int fd = ::open(existingCache.c_str(), O_RDONLY);
+ if ( fd < 0 )
+ return false;
+
+ // build map of found dylibs
+ std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
+ for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+ //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
+ currentDylibMap[aFile.runtimePath] = &aFile;
+ }
+
+ // make sure all dylibs in existing cache have same mtime and inode as found dylib
+ __block bool foundMismatch = false;
+ const uint64_t cacheMapLen = 0x40000000;
+ void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ( p != MAP_FAILED ) {
+ const DyldSharedCache* cache = (DyldSharedCache*)p;
+ cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
+ bool foundMatch = false;
+ auto pos = currentDylibMap.find(installName);
+ if ( pos != currentDylibMap.end() ) {
+ const DyldSharedCache::MappedMachO* foundDylib = pos->second;
+ if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
+ foundMatch = true;
+ }
+ }
+ if ( !foundMatch ) {
+ // use slow path and look for any dylib with a matching inode and mtime
+ bool foundSlow = false;
+ for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+ if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
+ foundSlow = true;
+ break;
+ }
+ }
+ if ( !foundSlow ) {
+ foundMismatch = true;
+ if ( verbose )
+ fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
+ }
+ }
+ });
+ ::munmap(p, cacheMapLen);
+ }
+
+ ::close(fd);
+
+ return !foundMismatch;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+ return (uint32_t)(abstime/1000/1000);
+}
+
+
+#define TERMINATE_IF_LAST_ARG( s ) \
+ do { \
+ if ( i == argc - 1 ) { \
+ fprintf(stderr, s ); \
+ return 1; \
+ } \
+ } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+ std::string rootPath;
+ std::string dylibListFile;
+ bool force = false;
+ std::string cacheDir;
+ std::unordered_set<std::string> archStrs;
+
+ dyld3::Platform platform = dyld3::Platform::iOS;
+
+ // parse command line options
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (strcmp(arg, "-debug") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-verbose") == 0) {
+ verbose = true;
+ }
+ else if (strcmp(arg, "-tvOS") == 0) {
+ platform = dyld3::Platform::tvOS;
+ }
+ else if (strcmp(arg, "-iOS") == 0) {
+ platform = dyld3::Platform::iOS;
+ }
+ else if (strcmp(arg, "-watchOS") == 0) {
+ platform = dyld3::Platform::watchOS;
+ }
+ else if ( strcmp(arg, "-runtime_dir") == 0 ) {
+ TERMINATE_IF_LAST_ARG("-runtime_dir missing path argument\n");
+ rootPath = 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 {
+ //usage();
+ fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
+ return 1;
+ }
+ }
+
+ if ( cacheDir.empty() ) {
+ fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
+ return 1;
+ }
+
+ if ( rootPath.empty() ) {
+ fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
+ return 1;
+ }
+ else {
+ // canonicalize rootPath
+ char resolvedPath[PATH_MAX];
+ if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+ rootPath = resolvedPath;
+ }
+ }
+
+ int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ if ( (err != 0) && (err != EEXIST) ) {
+ fprintf(stderr, "mkpath_np fail: %d", err);
+ return 1;
+ }
+
+ if ( archStrs.empty() ) {
+ switch ( platform ) {
+ case dyld3::Platform::iOS:
+ archStrs.insert("x86_64");
+ break;
+ case dyld3::Platform::tvOS:
+ archStrs.insert("x86_64");
+ break;
+ case dyld3::Platform::watchOS:
+ archStrs.insert("i386");
+ break;
+ case dyld3::Platform::unknown:
+ case dyld3::Platform::macOS:
+ assert(0 && "macOS does not have a simulator");
+ break;
+ case dyld3::Platform::bridgeOS:
+ assert(0 && "bridgeOS does not have a simulator");
+ break;
+ }
+ }
+
+ uint64_t t1 = mach_absolute_time();
+
+ // find all mach-o files for requested architectures
+ __block std::vector<MappedMachOsByCategory> allFileSets;
+ if ( archStrs.count("x86_64") )
+ allFileSets.push_back({"x86_64"});
+ if ( archStrs.count("i386") )
+ allFileSets.push_back({"i386"});
+ findAllFiles(rootPath, platform, allFileSets);
+ addMacOSAdditions(allFileSets);
+ for (MappedMachOsByCategory& fileSet : allFileSets) {
+ pruneCachedDylibs(rootPath, fileSet);
+ }
+
+ uint64_t t2 = mach_absolute_time();
+
+ fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+
+ // build all caches in parallel
+ __block bool cacheBuildFailure = false;
+ dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+ MappedMachOsByCategory& fileSet = allFileSets[index];
+ const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+ __block std::unordered_set<std::string> knownMissingDylib;
+
+ DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
+ std::string fullPath = rootPath + runtimePath;
+ struct stat statBuf;
+ if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+ std::vector<MappedMachOsByCategory> mappedFiles;
+ mappedFiles.push_back({fileSet.archName});
+ if ( addIfMachO(rootPath, runtimePath, statBuf, platform, mappedFiles) ) {
+ if ( !mappedFiles.back().dylibsForCache.empty() )
+ return mappedFiles.back().dylibsForCache.back();
+ }
+ }
+ if ( knownMissingDylib.count(runtimePath) == 0 ) {
+ fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s could not use in dylid cache: %s\n", fileSet.archName.c_str(), runtimePath.c_str());
+ knownMissingDylib.insert(runtimePath);
+ }
+ return DyldSharedCache::MappedMachO();
+ };
+ size_t startCount = fileSet.dylibsForCache.size();
+ std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
+ DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes);
+ for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
+ fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s not found in initial scan, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str());
+ }
+ for (auto& exclude : excludes) {
+ std::string reasons = "(\"";
+ for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
+ reasons += *i;
+ if (i != --exclude.second.end()) {
+ reasons += "\", \"";
+ }
+ }
+ reasons += "\")";
+ fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str());
+ fileSet.otherDylibsAndBundles.push_back(exclude.first);
+ }
+
+ // check if cache is already up to date
+ if ( !force ) {
+ if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
+ return;
+ }
+ fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+
+ // build cache new cache file
+ DyldSharedCache::CreateOptions options;
+ options.archName = fileSet.archName;
+ options.platform = platform;
+ options.excludeLocalSymbols = false;
+ options.optimizeStubs = false;
+ options.optimizeObjC = true;
+ options.codeSigningDigestMode = DyldSharedCache::SHA256only;
+ options.dylibsRemovedDuringMastering = false;
+ options.inodesAreSameAsRuntime = true;
+ options.cacheSupportsASLR = false;
+ options.forSimulator = true;
+ options.verbose = verbose;
+ options.evictLeafDylibsOnOverflow = true;
+ options.pathPrefixes = { rootPath };
+ DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+ // print any warnings
+ for (const std::string& warn : results.warnings) {
+ fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+ }
+ if ( !results.errorMessage.empty() ) {
+ // print error (if one)
+ fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
+ cacheBuildFailure = true;
+ }
+ else {
+ // save new cache file to disk and write new .map file
+ assert(results.cacheContent != nullptr);
+ if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+ cacheBuildFailure = true;
+ if ( !cacheBuildFailure ) {
+ std::string mapStr = results.cacheContent->mapFile();
+ std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+ safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+ }
+ // free created cache buffer
+ vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+ }
+ });
+
+ // we could unmap all input files, but tool is about to quit
+
+ return (cacheBuildFailure ? 1 : 0);
+}
+
+/*
+ * Registers a function to be called when the current thread terminates.
+ * Called by c++ compiler to implement destructors on thread_local object variables.
+ */
+extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
+
+
+/*
+ * Never called. On-disk thread local variables contain a pointer to this. Once
+ * the thread local is prepared, the pointer changes to a real handler such as tlv_get_addr.
+ */
+extern void _tlv_bootstrap() __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
+// FIXME: remove when Availability.h updated
+#ifndef __BRIDGEOS_UNAVAILABLE
+ #ifdef __ENVIRONMENT_BRIDGE_OS_VERSION_MIN_REQUIRED__
+ #define __BRIDGEOS_UNAVAILABLE __OS_AVAILABILITY(bridgeos,unavailable)
+ #else
+ #define __BRIDGEOS_UNAVAILABLE
+ #endif
+#endif
/*
* The following dyld API's are deprecated as of Mac OS X 10.5. They are either
typedef struct __NSObjectFileImage* NSObjectFileImage;
+
+
/* NSObjectFileImage can only be used with MH_BUNDLE files */
-extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-
-extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlclose()");
+
+extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()");
typedef struct __NSModule* NSModule;
-extern const char* NSNameOfModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern const char* NSLibraryNameForModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern const char* NSNameOfModule(NSModule m) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSLibraryNameForModule(NSModule m) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
-extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
#define NSLINKMODULE_OPTION_NONE 0x0
#define NSLINKMODULE_OPTION_BINDNOW 0x1
#define NSLINKMODULE_OPTION_PRIVATE 0x2
#define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8
#define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10
-extern bool NSUnLinkModule(NSModule module, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern bool NSUnLinkModule(NSModule module, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
#define NSUNLINKMODULE_OPTION_NONE 0x0
#define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1
#define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2
/* symbol API */
typedef struct __NSSymbol* NSSymbol;
-extern bool NSIsSymbolNameDefined(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern bool NSIsSymbolNameDefined(const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1
#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2
#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
-extern const char* NSNameOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern void * NSAddressOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern NSModule NSModuleForSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern const char* NSNameOfSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern void * NSAddressOfSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSModule NSModuleForSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dladdr()");
/* error handling API */
typedef enum {
NSOtherErrorInvalidArgs
} NSOtherErrorNumbers;
-extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()");
typedef struct {
void (*undefined)(const char* symbolName);
const char* fileName, const char* errorString);
} NSLinkEditErrorHandlers;
-extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
-extern bool NSAddLibrary(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern bool NSAddLibraryWithSearching(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern bool NSAddLibrary(const char* pathName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern bool NSAddLibraryWithSearching(const char* pathName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
#define NSADDIMAGE_OPTION_NONE 0x0
#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
#define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2
#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8
-extern bool _dyld_present(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool _dyld_launched_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool _dyld_all_twolevel_modules_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern void _dyld_bind_objc_module(const void* objc_module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool _dyld_bind_fully_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool _dyld_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-
-extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+extern bool _dyld_present(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "always true");
+extern bool _dyld_launched_prebound(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "moot");
+extern bool _dyld_all_twolevel_modules_prebound(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "moot");
+extern bool _dyld_bind_fully_image_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)");
+extern bool _dyld_image_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
+extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+
+extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
#if __cplusplus
#ifndef _DYLD_GDB_
#define _DYLD_GDB_
-/*
- * For Mac OS X 10.4 or later, use the interface in mach-o/dylib_images.h
- */
-#include <mach-o/dyld_images.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Prior to Mac OS 10.4, this is the interface gdb used to discover the mach-o images loaded in a process
- */
-#if __i386__
-/*
- * gdb_dyld_version is the version of gdb interface that dyld is currently
- * exporting. For the interface described in this header file gdb_dyld_version
- * is 2. As the gdb/dyld interface changes this number will be incremented and
- * comments will be added as to what are the are changes for the various
- * versions.
- */
-extern unsigned int gdb_dyld_version;
-
-/*
- * gdb_dyld_state_changed is the internal dyld routine called by dyld to notify
- * gdb that the state of the data structures has changed. gdb is expected to
- * put a break point on this routine and re-read the internal dyld data
- * structures below when this break point is hit.
- */
-extern void gdb_dyld_state_changed(void);
-
-/*
- * gdb looks directly at parts of two of dyld's internal data structures. The
- * list of object file images and the list of library images. The parts of
- * these structures that gdb looks at will not change unless the value of
- * gdb_dyld_version changes. The size of these structures and the other fields
- * that gdb does not look at may change.
- *
- * struct object_images {
- * struct object_image images[NOBJECT_IMAGES];
- * unsigned long nimages;
- * struct object_images *next_images;
- * ...
- * };
- *
- * struct library_images {
- * struct library_image images[NLIBRARY_IMAGES];
- * unsigned long nimages;
- * struct library_images *next_images;
- * ...
- * };
- *
- * Both the object_image structure and the library_image structure
- * start with a structure containing the following fields:
- *
- * struct image {
- * char *physical_name; physical image name (file name)
- * unsigned long vmaddr_slide; the slide from the staticly linked address
- * struct mach_header *mh; address of the mach header of the image
- * unsigned long valid; TRUE if this is struct is valid
- * char *name; image name for reporting errors
- * ...
- * };
- *
- * In gdb_dyld_version 1 the first field was "name". In gdb_dyld_version 2 the
- * first field was changed to "physical_name" and a new fifth field "name" was
- * added. These two fields are set to the same values except in the case of
- * zero-link. In zero-link the NSLinkModule() option
- * NSLINKMODULE_OPTION_TRAILING_PHYS_NAME is used and then the physical_name is
- * the file name of the module zero-link loaded that is part of the logical
- * image "name".
- */
-
-/* object_images is the global object_images structure */
-
-/* the number of gdb_object_image structures present per bucket */
-extern unsigned int gdb_nobject_images;
-
-/* the size of each gdb_object_image structure */
-extern unsigned int gdb_object_image_size;
-
-/* library_images is the global library_images structure */
-
-/* the number of gdb_library_image structures present per bucket */
-extern unsigned int gdb_nlibrary_images;
-
-/* the size of each gdb_library_image structure */
-extern unsigned int gdb_library_image_size;
-
-#endif
-
-
-
-#ifdef __cplusplus
-}
-#endif
#endif /* _DYLD_GDB_ */
#if __LP64__
uintptr_t reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
#else
- uintptr_t reserved[12-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+ uintptr_t reserved[13-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#endif
+ /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */
+ uintptr_t compact_dyld_image_info_addr;
+ size_t compact_dyld_image_info_size;
};
-
/*
* Beginning in Mac OS X 10.5, this is how gdb discovers where the shared cache is in a process.
* Images that are in the shared cache have their segments rearranged, so when using imageFilePath
uintptr_t length;
} ranges[4]; /* max regions */
};
-extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
+extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges __attribute__((visibility("hidden")));
#include <stdbool.h>
#include <Availability.h>
+#include <TargetConditionals.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
-//
-// private interface between libSystem.dylib and dyld
-//
-extern int _dyld_func_lookup(const char* dyld_func_name, void **address);
-
//
// private interface between libSystem.dylib and dyld
//
extern intptr_t _dyld_get_image_slide(const struct mach_header* mh);
-//
-// get pointer to this process's dyld_all_image_infos
-// 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() __attribute__((deprecated));
-
-
struct dyld_unwind_sections
{
// info->compact_unwind_section_length length of __TEXT/__unwind_info section
//
// Exists in Mac OS X 10.6 and later
+#if !__USING_SJLJ_EXCEPTIONS__
extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info);
+#endif
//
// This finds the SDK version that the main executable was built against.
// Returns zero on error, or if SDK version could not be determined.
//
-// Note on WatchOS, this returns the equivalent iOS SDK version number
-// (i.e an app built against WatchOS 2.0 SDK returne 9.0). To see the
+// Note on watchOS, this returns the equivalent iOS SDK version number
+// (i.e an app built against watchOS 2.0 SDK returne 9.0). To see the
// platform specific sdk version use dyld_get_program_sdk_watch_os_version().
//
// Exists in Mac OS X 10.8 and later
extern uint32_t dyld_get_program_sdk_version();
-// Watch OS only.
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+// watchOS only.
// This finds the Watch OS SDK version that the main executable was built against.
// Exists in Watch OS 2.0 and later
-extern uint32_t dyld_get_program_sdk_watch_os_version(); // __WATCHOS_AVAILABLE(2.0);
+extern uint32_t dyld_get_program_sdk_watch_os_version() __IOS_UNAVAILABLE __OSX_UNAVAILABLE __WATCHOS_AVAILABLE(2.0);
-// Watch OS only.
+// watchOS 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);
+#endif
+#if TARGET_OS_BRIDGE
+// bridgeOS only.
+// This finds the bridgeOS SDK version that the main executable was built against.
+// Exists in bridgeOSOS 2.0 and later
+extern uint32_t dyld_get_program_sdk_bridge_os_version();
+
+// bridgeOS 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 bridgeOS version (e.g. 2.0).
+// Exists in bridgeOS 2.0 and later
+extern uint32_t dyld_get_program_min_bridge_os_version();
+#endif
+
//
// 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.
uint64_t textSegmentSize;
uuid_t dylibUuid;
const char* path; // pointer invalid at end of iterations
+ // following fields all exist in version 2
+ uint64_t textSegmentOffset; // offset from start of cache
};
typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info;
+
+#ifdef __BLOCKS__
//
// 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.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));
+#endif /* __BLOCKS */
//
};
typedef struct dyld_abort_payload dyld_abort_payload;
+
+// These global variables are implemented in libdyld.dylib
+// Old programs that used crt1.o also defined these globals.
+// The ones in dyld are not used when an old program is run.
+extern int NXArgc;
+extern const char** NXArgv;
+extern char** environ; // POSIX says this not const, because it pre-dates const
+extern const char* __progname;
+
+
+// called by libSystem_initializer only
+extern void _dyld_initializer();
+
+// never called from source code. Used by static linker to implement lazy binding
+extern void dyld_stub_binder() __asm__("dyld_stub_binder");
+
+
+// called by exit() before it calls cxa_finalize() so that thread_local
+// objects are destroyed before global objects.
+extern void _tlv_exit();
+
+
+// temp exports to keep tapi happy, until ASan stops using dyldVersionNumber
+extern double dyldVersionNumber;
+extern const char* dyldVersionString;
+
#if __cplusplus
}
#endif /* __cplusplus */
// 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[]);
-void _dyld_debugger_notification(enum dyld_notify_mode, unsigned long count, uint64_t machHeaders[]);
struct dyld_process_cache_info {
+++ /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"
-
-#include "MachOProxy.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, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, macho_header<P>* mh, MachOProxy* proxy);
-
- 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::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR);
-
-private:
- typedef typename P::uint_t pint_t;
- typedef typename P::E E;
-
- 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;
- MachOProxy* _proxy;
- 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;
- const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& _segmentMap;
- 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, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, macho_header<P>* mh, MachOProxy* proxy)
- : _cacheBuffer(cacheBuffer)
- , _mh(mh)
- , _segmentMap(segmentMap)
- , _proxy(proxy)
- , _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);
- }
-}
-
-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);
- }
-
-
- // 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 info = _proxy->symbolInfo(symbolName);
- if (info != nullptr) {
- if (info->isSymbolReExport) {
- const char* importName = symbolName;
- if (!info->reExportName.empty())
- importName = info->reExportName.c_str();
- std::string& depPath = _dependentPaths[info->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 = (pint_t)_proxy->addressOf(symbolName, _segmentMap);
- *foundIn = this;
- *isResolverSymbol = info->isResolver;
- *isAbsolute = info->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>
-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;
- const auto info = _proxy->symbolInfo(resolverSymbolName);
- if (info) {
- 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::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR)
-{
- std::unordered_map<std::string, BindInfo<P>*> dylibPathToBindInfo;
- std::unordered_map<macho_header<P>*, BindInfo<P>*> headersToBindInfo;
- for (auto& dylib : dylibs) {
- auto dylibI = segmentMap.find(dylib);
- assert(dylibI != segmentMap.end());
- macho_header<P>* mh = (macho_header<P>*)((uint8_t*)cacheBuffer + dylibI->second[0].cacheFileOffset);
- if (headersToBindInfo.count(mh) == 0) {
- headersToBindInfo[mh] = new BindInfo<P>(cacheBuffer, segmentMap, mh, dylib);
- }
- dylibPathToBindInfo[dylib->installName] = headersToBindInfo[mh];
- for (const auto& alias : dylib->installNameAliases) {
- dylibPathToBindInfo[alias] = 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);
- }
-
- // clean up
- for (const auto& entry: headersToBindInfo) {
- delete entry.second;
- }
-}
-
-
-} // anonymous namespace
-
-void SharedCache::bindAllImagesInCache(const std::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR)
-{
- switch ( _arch.arch ) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_I386:
- BindInfo<Pointer32<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibs, segmentMap, pointersForASLR);
- break;
- case CPU_TYPE_X86_64:
- case CPU_TYPE_ARM64:
- BindInfo<Pointer64<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibs, segmentMap, 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_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
-}
-
-
-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) {
- entries[normalizedPath] = fill(normalizedPath);
- }
- });
-}
-
-std::tuple<uint8_t *, struct stat, bool> FileCache::cacheLoad(const std::string path) {
- bool found = false;
- std::tuple<uint8_t*, struct stat, bool> retval;
- std::string normalizedPath = normalize_absolute_file_path(path);
- cacheBuilderDispatchSync(cache_queue, [=, &found, &retval] {
- auto entry = entries.find(normalizedPath);
- if (entry != entries.end()) {
- retval = entry->second;
- found = true;
- }
- });
-
- if (!found) {
- auto info = fill(normalizedPath);
- cacheBuilderDispatchSync(cache_queue, [=, &found, &retval] {
- auto entry = entries.find(normalizedPath);
- if (entry != entries.end()) {
- retval = entry->second;
- }else {
- retval = entries[normalizedPath] = info;
- retval = info;
- }
- });
- }
-
- return retval;
-}
-
-//FIXME error handling
-std::tuple<uint8_t*, struct stat, bool> 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);
- return std::make_tuple((uint8_t*)(-1), stat_buf, false);
- }
-
- if ( fstat(fd, &stat_buf) == -1) {
- verboseLog("can't stat open file '%s', errno=%d", path.c_str(), errno);
- ::close(fd);
- return std::make_tuple((uint8_t*)(-1), stat_buf, false);
- }
-
- if ( stat_buf.st_size < 4096 ) {
- verboseLog("file too small '%s'", path.c_str());
- ::close(fd);
- return std::make_tuple((uint8_t*)(-1), stat_buf, false);
- }
-
- 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 file at %s failed, errno=%d", path.c_str(), errno);
- ::close(fd);
- return std::make_tuple((uint8_t*)(-1), stat_buf, false);
- }
-
- ::close(fd);
-
- //PERF-HACK: touch bits of the MachO before we need them to speed things up on a spinning disk
- madvise((void*)buffer_ptr, 4096, MADV_WILLNEED);
-
- //if it is fat we need to touch each architecture
- const fat_header* fh = (fat_header*)buffer_ptr;
- if ( OSSwapBigToHostInt32( fh->magic ) == FAT_MAGIC ) {
- 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 ) {
- uint32_t fileOffset = OSSwapBigToHostInt32( slices[i].offset );
- madvise((void*)((uint8_t *)buffer_ptr+fileOffset), 4096, MADV_WILLNEED);
- }
- }
-
- return std::make_tuple((uint8_t*)buffer_ptr, stat_buf, rootlessProtected);
-}
-
-
-
-
-
-
+++ /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"
-
-//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)
-{
- dispatch_async(getLogQueue(), ^{
- (void)fprintf(fd, "%s", str);
- free((void*)str);
- });
-}
-
-#define VLOG(header) \
- va_list list; \
- va_start(list, format); \
- char *temp, *temp2; \
- vasprintf(&temp, format, list); \
- auto ctx = getLoggingContext(); \
- if (ctx && !ctx->name().empty()) { \
- asprintf(&temp2, "[%s] %s%s\n", ctx->name().c_str(), header, temp); \
- } else { \
- asprintf(&temp2, "%s%s\n", header, temp); \
- } \
- free(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* blockTemp;
- vasprintf(&blockTemp, format, list);
-
- auto ctx = getLoggingContext();
- if (ctx) {
- for (auto& target : ctx->targets().second) {
- ctx->targets().first->configuration(target.first).architecture(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(temp2);
- } 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) {
- if (context) {
- WarningTargets warningTargets = context->targets();
- for (auto& target : warningTargets.second) {
- warningTargets.first->configuration(target.first).architecture(target.second).results.failure = exception;
- }
- 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 "Trie.hpp"
-#include "MachOProxy.h"
-
-#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
-#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
-#endif
-
-namespace {
-std::vector<MachOProxy*> mapMachOFile(const std::string& buildPath, const std::string& path)
-{
- std::vector<MachOProxy*> retval;
- const uint8_t* p = (uint8_t*)( -1 );
- struct stat stat_buf;
- bool rootless;
-
- std::tie(p, stat_buf, rootless) = fileCache.cacheLoad(buildPath);
-
- 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.push_back(new MachOProxy(buildPath, path, stringForArch(arch), stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless));
- //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.push_back(new MachOProxy(buildPath, path, stringForArch(arch), stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless));
- //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;
-}
-
-} /* Anonymous namespace */
-
-template <typename P>
-std::vector<std::string> MachOProxy::dependencies()
-{
- const uint8_t* buffer = getBuffer();
- const macho_header<P>* mh = (const macho_header<P>*)buffer;
- 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;
- std::vector<std::string> retval;
-
- 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: {
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- std::string depName = dylib->name();
-
- retval.push_back(depName);
- } break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
- }
-
- return retval;
-}
-
-template <typename P>
-std::vector<std::string> MachOProxy::reexports()
-{
- const uint8_t* buffer = getBuffer();
- const macho_header<P>* mh = (const macho_header<P>*)buffer;
- 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;
- std::vector<std::string> retval;
-
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_REEXPORT_DYLIB: {
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- std::string depName = dylib->name();
-
- retval.push_back(depName);
- } break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
- }
-
- return retval;
-}
-
-std::vector<std::string> MachOProxy::dependencies()
-{
- switch (archForString(arch).arch) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_I386:
- return dependencies<Pointer32<LittleEndian>>();
- case CPU_TYPE_X86_64:
- case CPU_TYPE_ARM64:
- return dependencies<Pointer64<LittleEndian>>();
- break;
- default:
- return std::vector<std::string>();
- }
-}
-
-std::vector<std::string> MachOProxy::reexports()
-{
- switch (archForString(arch).arch) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_I386:
- return reexports<Pointer32<LittleEndian>>();
- case CPU_TYPE_X86_64:
- case CPU_TYPE_ARM64:
- return reexports<Pointer64<LittleEndian>>();
- break;
- default:
- return std::vector<std::string>();
- }
-}
-
-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 macho_dyld_info_command<P>* dyldInfo = nullptr;
- const uint32_t cmd_count = mh->ncmds();
- const macho_load_command<P>* cmd = cmds;
- uint64_t baseAddr = 0;
- _filetype = mh->filetype();
- if (_filetype == MH_DYLIB_STUB) {
- return "stub dylib";
- }
- if (_filetype == MH_DSYM) {
- return "DSYM";
- }
- 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();
- addAlias(path);
- } break;
- case LC_UUID: {
- const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
- uuid = UUID(uuidCmd->uuid());
- } 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 ( isExecutable() && 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)";
- }
- } break;
- case macho_segment_command<P>::CMD: {
- const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- MachOProxySegment seg;
- seg.name = segCmd->segname();
- seg.size = align(segCmd->vmsize(), 12);
- seg.vmaddr = segCmd->vmaddr();
- 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);
- if (seg.name == "__TEXT") {
- baseAddr = seg.vmaddr;
- }
- } 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:
- dyldInfo = (macho_dyld_info_command<P>*)cmd;
- hasDylidInfo = true;
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
- }
-
- identifier = uuid;
-
- if (!hasDylidInfo) {
- return "built for old OS";
- }
-
- if (dyldInfo && dyldInfo->bind_size() != 0) {
- _bind_offset = dyldInfo->bind_off();
- _bind_size = dyldInfo->bind_size();
- }
-
- if (dyldInfo && dyldInfo->lazy_bind_size() != 0) {
- _lazy_bind_offset = dyldInfo->lazy_bind_off();
- _lazy_bind_size = dyldInfo->lazy_bind_size();
- }
-
- // if no export info, no _exports map to build
- if (dyldInfo && dyldInfo->export_size() != 0) {
- std::vector<ExportInfoTrie::Entry> exports;
- const uint8_t* exportsStart = &buffer[dyldInfo->export_off()];
- const uint8_t* exportsEnd = &exportsStart[dyldInfo->export_size()];
- if (!ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports)) {
- terminate("malformed exports trie in %s", path.c_str());
- }
-
- for (const ExportInfoTrie::Entry& entry : exports) {
- if (!_exports[entry.name].isAbsolute) {
- for (const auto& seg : segments) {
- if (seg.size > 0 && (seg.vmaddr - baseAddr) <= entry.info.address && entry.info.address < (seg.vmaddr - baseAddr) + seg.size) {
- _exports[entry.name].segmentOffset = entry.info.address - (seg.vmaddr - baseAddr);
- _exports[entry.name].segmentName = seg.name;
- break;
- }
- }
- } else {
- _exports[entry.name].segmentOffset = (uint64_t)entry.info.address;
- _exports[entry.name].segmentName = "";
- }
-
- 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;
- break;
- default:
- terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), path.c_str());
- break;
- }
- }
- }
-
- if (!isDylib()) {
- return "";
- }
-
- 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 (_filetype == MH_DYLIB);
-}
-
-const bool MachOProxy::isExecutable()
-{
- return (_filetype == MH_EXECUTE);
-}
-
-static std::map<ImageIdentifier, MachOProxy*> identifierMap;
-std::map<std::pair<std::string, std::string>, MachOProxy*> archMap;
-static dispatch_queue_t identifierQueue;
-
-MachOProxy* MachOProxy::forIdentifier(const ImageIdentifier& identifier, const std::string preferredArch)
-{
- auto i = identifierMap.find(identifier);
- // We need an identifier
- if (i == identifierMap.end())
- return nullptr;
-
- // Is the identifier the arch we want?
- if (i->second->arch == preferredArch)
- return i->second;
-
- // Fallback to a slow path to try to find a best fit
- return forInstallnameAndArch(i->second->installName, preferredArch);
-}
-
-MachOProxy* MachOProxy::forInstallnameAndArch(const std::string& installname, const std::string& arch)
-{
- auto i = archMap.find(std::make_pair(installname, arch));
- if (i == archMap.end())
- i = archMap.find(std::make_pair(installname, fallbackArchStringForArchString(arch)));
- if (i != archMap.end())
- return i->second;
- return nullptr;
-}
-
-void MachOProxy::mapDependencies()
-{
- // Build a complete map of all installname/alias,archs to their proxies
- runOnAllProxies(false, [&](MachOProxy* proxy) {
- archMap[std::make_pair(proxy->path, proxy->arch)] = proxy;
- for (auto& alias : proxy->installNameAliases) {
- archMap[std::make_pair(alias, proxy->arch)] = proxy;
- }
- });
-
- //Wire up the dependencies
- runOnAllProxies(false, [&](MachOProxy* proxy) {
- auto dependencyInstallnames = proxy->dependencies();
- for (auto dependencyInstallname : dependencyInstallnames) {
- auto dependencyProxy = forInstallnameAndArch(dependencyInstallname, proxy->arch);
- if (dependencyProxy == nullptr) {
- proxy->error = "Missing dependency: " + dependencyInstallname;
- } else {
- proxy->requiredIdentifiers.push_back(dependencyProxy->identifier);
- dependencyProxy->dependentIdentifiers.push_back(proxy->identifier);
- }
- }
-
- auto reexportInstallnames = proxy->reexports();
- for (auto reexportInstallname : reexportInstallnames) {
- auto reexportProxy = forInstallnameAndArch(reexportInstallname, proxy->arch);
- if (reexportProxy == nullptr) {
- proxy->error = "Missing reexport dylib: " + reexportInstallname;
- } else {
- proxy->_reexportProxies.push_back(reexportProxy);
- }
- }
-
- });
-}
-
-void MachOProxy::runOnAllProxies(bool concurrently, std::function<void(MachOProxy* proxy)> lambda)
-{
- dispatch_group_t runGroup = dispatch_group_create();
- dispatch_queue_t runQueue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, NULL);
-
- for (auto& identifier : identifierMap) {
- if (concurrently) {
- cacheBuilderDispatchGroupAsync(runGroup, runQueue, [&] {
- lambda(identifier.second);
- });
- } else {
- lambda(identifier.second);
- }
- }
-
- dispatch_group_wait(runGroup, DISPATCH_TIME_FOREVER);
-}
-
-std::map<std::string, MachOProxy*> MachOProxy::loadProxies(const std::string& buildPath, const std::string& path, bool warnOnProblems, bool ignoreUncacheableDylibsInExecutables)
-{
- std::vector<MachOProxy*> slices = mapMachOFile(buildPath, path);
- std::map<std::string, MachOProxy*> retval;
-
- for ( auto& slice : slices ) {
- std::string errorMessage;
- verboseLog( "analyzing file '%s'", path.c_str() );
- switch (archForString(slice->arch).arch) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_I386:
- errorMessage = slice->machoParser<Pointer32<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
- break;
- case CPU_TYPE_X86_64:
- case CPU_TYPE_ARM64:
- errorMessage = slice->machoParser<Pointer64<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
- break;
- default:
- errorMessage = "unsupported arch '" + slice->arch + "'";
- break;
- }
-
- if (errorMessage.empty()) {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- identifierQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.ids", DISPATCH_QUEUE_SERIAL);
- });
- retval[slice->arch] = slice;
- dispatch_sync(identifierQueue, ^{
- identifierMap[slice->identifier] = slice;
- });
- } else {
- if (warnOnProblems)
- warning("%s (%s)", errorMessage.c_str(), path.c_str());
- }
- }
-
- return retval;
-}
-
-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(buildPath);
- 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>
-
-#include "mega-dylib-utils.h"
-
-struct MachOProxy;
-
-struct MachOProxySegment {
- std::string name;
- uint64_t size;
- uint64_t sizeOfSections;
- uint64_t vmaddr;
- uint32_t diskSize;
- uint32_t fileOffset;
- uint8_t p2align;
- uint8_t protection;
-};
-
-struct MachOProxy {
- MachOProxy(const std::string& bp, const std::string& p, const std::string& a, ino_t i, time_t t, uint32_t o, uint32_t s, bool r)
- : buildPath(bp)
- , path(p)
- , arch(a)
- , fatFileOffset(o)
- , fileSize(s)
- , lastModTime(t)
- , inode(i)
- , installNameOffsetInTEXT(0)
- , rootlessProtected(r)
- , _bind_offset(0)
- , _bind_size(0)
- , queue(dispatch_queue_create("com.apple.dyld.proxy", NULL))
- {
- }
-
- struct SymbolInfo {
- SymbolInfo() {}
- std::string segmentName;
- uint64_t segmentOffset = 0;
- bool isResolver = false;
- bool isAbsolute = false;
- bool isSymbolReExport = false;
- bool isThreadLocal = false;
- int reExportDylibIndex = 0;
- std::string reExportName;
- };
-
- const std::string buildPath;
- const std::string path;
- const std::string arch;
- const uint32_t fatFileOffset;
- const uint32_t fileSize;
- const time_t lastModTime;
- const ino_t inode;
- const bool rootlessProtected;
- dispatch_queue_t queue;
- std::string installName;
- std::set<std::string> installNameAliases;
- uint32_t installNameOffsetInTEXT;
- std::string error;
- std::vector<ImageIdentifier> requiredIdentifiers;
- std::vector<ImageIdentifier> dependentIdentifiers;
- UUID uuid;
- ImageIdentifier identifier;
- std::vector<MachOProxySegment> segments;
-
- const uint8_t* getBuffer();
- const uint8_t* getBindStart() { return &(getBuffer())[_bind_offset]; }
- const uint8_t* getBindEnd() { return &(getBuffer())[_bind_offset + _bind_size]; }
- const uint8_t* getLazyBindStart() { return &(getBuffer())[_lazy_bind_offset]; }
- const uint8_t* getLazyBindEnd() { return &(getBuffer())[_lazy_bind_offset + _lazy_bind_size]; }
-
- const bool isDylib();
- const bool isExecutable();
- bool addAlias(const std::string& alias);
- static void mapDependencies();
-
- const uint64_t addressOf(const std::string& symbol, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap)
- {
- auto info = symbolInfo(symbol);
- assert(info != nullptr);
- if (info->isAbsolute)
- return info->segmentOffset;
- auto proxyI = segmentMap.find(this);
- assert(proxyI != segmentMap.end());
-
- for (const auto& seg : proxyI->second) {
- if (seg.base->name == info->segmentName) {
- assert(!info->segmentName.empty());
- return seg.address + info->segmentOffset;
- }
- }
-
- return 0;
- }
-
- SymbolInfo* symbolInfo(const std::string& symbol)
- {
- auto i = _exports.find(symbol);
- if (i != _exports.end())
- return &i->second;
- return nullptr;
- }
-
- bool providesSymbol(const std::string& symbol)
- {
- if (_exports.find(symbol) != _exports.end())
- return true;
-
- for (const auto& proxy : _reexportProxies) {
- if (proxy->providesSymbol(symbol))
- return true;
- }
- return false;
- }
- static std::map<std::string, MachOProxy*> loadProxies(const std::string& prefix, const std::string& path, bool warnOnProblems = false, bool ignoreUncacheableDylibsInExecutables = false);
- static void runOnAllProxies(bool concurrently, std::function<void(MachOProxy* proxy)> lambda);
- static MachOProxy* forIdentifier(const ImageIdentifier& identifier, const std::string preferredArch);
- static MachOProxy* forInstallnameAndArch(const std::string& installname, const std::string& arch);
-
- std::vector<std::string> dependencies();
- std::vector<std::string> reexports();
-
-private:
- uint32_t _filetype;
- std::map<std::string, SymbolInfo> _exports;
- uint32_t _bind_offset;
- uint32_t _bind_size;
- uint32_t _lazy_bind_offset;
- uint32_t _lazy_bind_size;
- std::vector<MachOProxy*> _reexportProxies;
-
- template <typename P>
- std::string machoParser(bool ignoreUncacheableDylibsInExecutables);
-
- template <typename P>
- std::vector<std::string> dependencies();
-
- template <typename P>
- std::vector<std::string> reexports();
-};
-
-
-#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>
-
-#include <assert.h>
-
-struct MachOProxy;
-
-extern void terminate(const char* format, ...) __printflike(1, 2) __attribute__((noreturn));
-extern std::string toolDir();
-
-struct SharedCache;
-struct Manifest;
-
-struct Manifest {
- struct Project {
- std::vector<std::string> sources;
- };
-
- struct File {
- MachOProxy* proxy;
-
- File( MachOProxy* P ) : proxy( P ) {}
- };
-
- struct Anchor {
- ImageIdentifier identifier;
- bool required;
- Anchor( const ImageIdentifier& I ) : identifier( I ) {}
- };
-
- 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 uuid;
- std::string installname;
- std::vector<SegmentInfo> segments;
- DylibInfo(void) : included(true) {}
- };
-
- struct Results {
- std::string failure;
- std::map<ImageIdentifier, DylibInfo> dylibs;
- std::vector<std::string> warnings;
- CacheInfo developmentCache;
- CacheInfo productionCache;
- DylibInfo& dylibForInstallname(const std::string& installname)
- {
- auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<ImageIdentifier, DylibInfo> d) { return d.second.installname == installname; });
- assert(i != dylibs.end());
- return i->second;
- }
- void exclude(MachOProxy* proxy, const std::string& reason);
- };
-
- struct Architecture {
- std::vector<Anchor> anchors;
- mutable Results results;
-
- bool operator==(const Architecture& O) const
- {
- 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
- || Odylib->second.uuid != dylib.second.uuid)
- return false;
- }
- }
-
- for (const 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
- || dylib->second.uuid != Odylib.second.uuid)
- return false;
- }
- }
-
- return true;
- }
-
- bool operator!=(const Architecture& other) const { return !(*this == other); }
- };
-
- 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;
-
- bool operator==(const Configuration& O) const
- {
- return architectures == O.architectures;
- }
-
- bool operator!=(const Configuration& other) const { return !(*this == other); }
-
- const Architecture& architecture(const std::string& architecture) const
- {
- assert(architectures.find(architecture) != architectures.end());
- return architectures.find(architecture)->second;
- }
-
- void forEachArchitecture(std::function<void(const std::string& archName)> lambda)
- {
- for (const auto& architecutre : architectures) {
- lambda(architecutre.first);
- }
- }
- };
-
- const std::map<std::string, Project>& projects()
- {
- return _projects;
- }
-
- const Configuration& configuration(const std::string& configuration) const
- {
- assert(_configurations.find(configuration) != _configurations.end());
- return _configurations.find(configuration)->second;
- }
-
- void forEachConfiguration(std::function<void(const std::string& configName)> lambda)
- {
- for (const auto& configuration : _configurations) {
- lambda(configuration.first);
- }
- }
-
- void addProjectSource(const std::string& project, const std::string& source, bool first = false)
- {
- auto& sources = _projects[project].sources;
- if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
- if (first) {
- sources.insert(sources.begin(), source);
- } else {
- sources.push_back(source);
- }
- }
- }
-
- const std::string projectPath(const std::string& projectName)
- {
- auto project = _projects.find(projectName);
- if (project == _projects.end())
- return "";
- if (project->second.sources.size() == 0)
- return "";
- return project->second.sources[0];
- }
-
-
- const bool empty(void) {
- for (const auto& configuration : _configurations) {
- if (configuration.second.architectures.size() != 0)
- return false;
- }
- return true;
- }
-
- const std::string dylibOrderFile() const { return _dylibOrderFile; };
- void setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
-
- const std::string dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
- void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
-
- const std::string metabomFile() const { return _metabomFile; };
- void setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
-
- const std::string platform() const { return _platform; };
- void setPlatform(const std::string& platform) { _platform = platform; };
-
- const std::string& build() const { return _build; };
- void setBuild(const std::string& build) { _build = build; };
- const uint32_t version() const { return _manifestVersion; };
- void setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
- bool normalized;
-
- Manifest(void) {}
- Manifest(const std::set<std::string>& archs, const std::string& overlayPath, const std::string& rootPath, const std::set<std::string>& paths);
-#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);
- bool sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture,
- const std::string& path) const;
- void remove(const std::string& config, const std::string& arch);
- MachOProxy* removeLargestLeafDylib(const std::string& configuration, const std::string& architecture);
- bool checkLinks();
- void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
- bool filterForConfig(const std::string& configName);
-
-private:
- uint32_t _manifestVersion;
- std::string _build;
- std::string _dylibOrderFile;
- std::string _dirtyDataOrderFile;
- std::string _metabomFile;
- std::string _platform;
- std::map<std::string, Project> _projects;
- std::map<std::string, Configuration> _configurations;
- void removeDylib(MachOProxy* proxy, const std::string& reason, const std::string& configuration, const std::string& architecture,
- std::unordered_set<ImageIdentifier>& processedIdentifiers);
- void calculateClosure(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);
- MachOProxy* dylibProxy(const std::string& installname, const std::string& arch);
-};
-
-
-#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 "dsc_iterator.h"
-#include "MachOProxy.h"
-#include "mega-dylib-utils.h"
-
-#include "Manifest.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()]; }
-
-std::string fileExists(const std::string& path)
-{
- const uint8_t* p = (uint8_t*)(-1);
- struct stat stat_buf;
-
- std::tie(p, stat_buf, rootless) = fileCache.cacheLoad(path);
- if (p != (uint8_t*)(-1)) {
- return normalize_absolute_file_path(path);
- }
-
- return "";
-}
-
-} /* Anonymous namespace */
-
-void Manifest::Results::exclude(MachOProxy* proxy, const std::string& reason)
-{
- dylibs[proxy->identifier].uuid = proxy->uuid;
- dylibs[proxy->identifier].installname = proxy->installName;
- dylibs[proxy->identifier].included = false;
- dylibs[proxy->identifier].exclusionInfo = reason;
-}
-
-Manifest::Manifest(const std::set<std::string>& archs, const std::string& overlayPath, const std::string& rootPath, const std::set<std::string>& paths)
-{
- 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::loadProxies(fullPath, path);
-
- for (const auto& arch : archs) {
- auto proxyI = proxies.find(arch);
- if (proxyI == proxies.end())
- proxyI = proxies.find(fallbackArchStringForArchString(arch));
- if (proxyI == proxies.end())
- continue;
-
- auto dependecies = proxyI->second->dependencies();
- for (const auto& dependency : dependecies) {
- unprocessedPaths.insert(dependency);
- }
- _configurations["localhost"].architectures[arch].anchors.push_back(proxyI->second->identifier);
- }
-
- //Stuff
- }
-
- pathsToProcess.clear();
- std::set_difference(unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(),
- std::inserter(pathsToProcess, pathsToProcess.begin()));
- }
- MachOProxy::mapDependencies();
-}
-
-#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;
- std::vector<std::pair<std::string, MachOProxy*>> configProxies;
-
- setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
-
- for (NSString* project in manifestDict[@"projects"]) {
- for (NSString* source in manifestDict[@"projects"][project]) {
- addProjectSource([project UTF8String], [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];
- }
-
- setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
- setBuild([manifestDict[@"build"] UTF8String]);
- if (manifestDict[@"dylibOrderFile"]) {
- setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
- }
- if (manifestDict[@"dirtyDataOrderFile"]) {
- setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
- }
-
- auto metabom = MBMetabomOpen(metabomFile().c_str(), false);
- auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
- MBEntry entry;
-
- auto bomSemaphore = dispatch_semaphore_create(32);
- auto bomGroup = dispatch_group_create();
- auto bomQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.bom", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
- auto archQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.arch", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
- auto manifestQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.arch", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
-
- // FIXME error handling (NULL metabom)
-
- //First we iterate through the bom and build our objects
-
- while ((entry = MBIteratorNext(metabomEnumerator))) {
- dispatch_semaphore_wait(bomSemaphore, DISPATCH_TIME_FOREVER);
- cacheBuilderDispatchGroupAsync(bomGroup, manifestQueue, [this, &bomSemaphore, &archQueue, &bomGroup, &bomQueue, &metabom, entry, &overlays, &metabomTagMap, &metabomRestrictedTagMap, &metabomExcludeTagMap, &manifestDict, &configProxies] {
- BOMFSObject fsObject = nullptr;
- std::string entryPath;
- BOMFSObjType entryType;
- cacheBuilderDispatchSync(bomQueue, [&entry, &fsObject, &entryPath, &entryType] {
- fsObject = MBEntryGetFSObject(entry);
- entryPath = BOMFSObjectPathName(fsObject);
- if (entryPath[0] == '.') {
- entryPath.erase(0, 1);
- }
- entryType = BOMFSObjectType(fsObject);
- });
-
- MBTag tag;
- auto tagCount = MBEntryGetNumberOfProjectTags(entry);
- if ( entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0 ) {
- 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) {
- cacheBuilderDispatchSync(bomQueue, [i, &metabom, &tagStrs, &tagStrMap, &tags] {
- 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;
- cacheBuilderDispatchSync(bomQueue, [&projectName, &metabom, &tag] {
- projectName = MBMetabomGetProjectForTag(metabom, tag);
- });
-
- std::map<std::string, MachOProxy*> proxies;
- for (const auto& overlay : overlays) {
- proxies = MachOProxy::loadProxies(overlay + "/" + entryPath, entryPath);
- if (proxies.size() > 0)
- break;
- }
-
- if (proxies.size() == 0) {
- proxies = MachOProxy::loadProxies(projectPath(projectName) + "/" + entryPath, entryPath);
- }
-
- tagCount = MBEntryGetNumberOfPackageTags(entry);
- MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
- MBEntryGetPackageTags(entry, tags);
- std::set<std::string> tagStrs;
-
- cacheBuilderDispatchSync(bomQueue, [&] {
- for (auto i = 0; i < tagCount; ++i) {
- tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
- }
- });
-
- for (auto& proxy : proxies) {
- 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;
- }
- cacheBuilderDispatchGroupAsync(bomGroup, archQueue, [this, &manifestDict, &configProxies, configuration, proxy, tagStr] {
- if ([manifestDict[@"configurations"][cppToObjStr(configuration->second)][@"architectures"]
- containsObject:cppToObjStr(proxy.second->arch)]) {
- _configurations[configuration->second].architectures[proxy.second->arch].anchors.push_back(proxy.second->identifier);
- }
- });
- }
- }
- }
- dispatch_semaphore_signal(bomSemaphore);
- });
- }
-
- dispatch_group_wait(bomGroup, DISPATCH_TIME_FOREVER);
- MBIteratorFree(metabomEnumerator);
- MBMetabomFree(metabom);
- MachOProxy::mapDependencies();
-}
-
-#endif
-
-template <typename P>
-bool checkLink(MachOProxy* proxy, const uint8_t* p, const uint8_t* end)
-{
- bool retval = true;
- std::vector<std::string> dylibs = proxy->dependencies();
-
- std::string symbolName;
- int libraryOrdinal = 0;
- 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:
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- (void)read_sleb128(p, end);
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- case BIND_OPCODE_ADD_ADDR_ULEB:
- (void)read_uleb128(p, end);
- break;
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- (void)read_uleb128(p, end);
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- (void)read_uleb128(p, end);
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- case BIND_OPCODE_DO_BIND: {
- if (libraryOrdinal <= 0)
- break;
- if ( libraryOrdinal > dylibs.size() ) {
- warning("Illegal library ordinal (%d) in dylib %s bind opcode (max ordinal %lu)", libraryOrdinal, proxy->path.c_str(), dylibs.size());
- retval = false;
- }
- else {
- auto dependencyProxy = MachOProxy::forInstallnameAndArch(dylibs[libraryOrdinal - 1], proxy->arch);
- if (!weakImport && (!dependencyProxy || !dependencyProxy->providesSymbol(symbolName))) {
- warning("Could not find symbol %s in dylib %s for %s", symbolName.c_str(), dylibs[libraryOrdinal - 1].c_str(), proxy->path.c_str());
- retval = false;
- }
- }
- } break;
- default:
- warning("bad bind opcode in binary 0x%02X in %s", *p, proxy->path.c_str());
- }
- }
-
- return retval;
-}
-
-bool checkLink(MachOProxy* proxy)
-{
- switch (archForString(proxy->arch).arch) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_I386:
- return (checkLink<Pointer32<LittleEndian>>(proxy, proxy->getBindStart(), proxy->getBindEnd())
- && checkLink<Pointer32<LittleEndian>>(proxy, proxy->getLazyBindStart(), proxy->getLazyBindEnd()));
- case CPU_TYPE_ARM64:
- case CPU_TYPE_X86_64:
- return (checkLink<Pointer64<LittleEndian>>(proxy, proxy->getBindStart(), proxy->getBindEnd())
- && checkLink<Pointer64<LittleEndian>>(proxy, proxy->getLazyBindStart(), proxy->getLazyBindEnd()));
- default:
- terminate("unsupported arch 0x%08X", archForString(proxy->arch).arch);
- }
-}
-
-bool Manifest::checkLinks()
-{
- dispatch_queue_t linkCheckQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, NULL);
- dispatch_semaphore_t linkCheckSemphore = dispatch_semaphore_create(32);
-
- dispatch_group_t linkCheckGroup = dispatch_group_create();
-
- runConcurrently(linkCheckQueue, linkCheckSemphore, [this](const std::string configuration, const std::string architecture) {
- for (const auto& anchor : this->configuration(configuration).architecture(architecture).anchors) {
- const auto identifier = anchor.identifier;
- const auto proxy = MachOProxy::forIdentifier(identifier, architecture);
- if (proxy->isExecutable()) {
- checkLink(proxy);
- }
- }
- });
-
- dispatch_group_wait(linkCheckGroup, DISPATCH_TIME_FOREVER);
-
- return true;
-}
-
-void Manifest::runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda)
-{
- dispatch_group_t runGroup = dispatch_group_create();
- for (auto& config : _configurations) {
- for (auto& architecture : config.second.architectures) {
- dispatch_semaphore_wait(concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER);
- cacheBuilderDispatchGroupAsync(runGroup, queue, [&] {
- WarningTargets targets;
- targets.first = this;
- 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);
-}
-
-bool Manifest::filterForConfig(const std::string& configName)
-{
- for (const auto configuration : _configurations) {
- if (configName == configuration.first) {
- std::map<std::string, Configuration> filteredConfigs;
- filteredConfigs[configName] = configuration.second;
-
- _configurations = filteredConfigs;
-
- for (auto& arch : configuration.second.architectures) {
- arch.second.results = Manifest::Results();
- }
- return true;
- }
- }
- return false;
-}
-
-void Manifest::calculateClosure(bool enforceRootless)
-{
- auto closureSemaphore = dispatch_semaphore_create(32);
- auto closureGroup = dispatch_group_create();
- auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
- rootless = enforceRootless;
-
- for (auto& config : _configurations) {
- for (auto& arch : config.second.architectures) {
- dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
- cacheBuilderDispatchGroupAsync(closureGroup, closureQueue, [&] {
- calculateClosure(config.first, arch.first);
- dispatch_semaphore_signal(closureSemaphore);
- });
- }
- }
-
- dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
-}
-
-void Manifest::remove(const std::string& config, const std::string& arch)
-{
- if (_configurations.count(config))
- _configurations[config].architectures.erase(arch);
-}
-
-bool
-Manifest::sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture, const std::string& path) const {
- std::set<std::pair<std::string, UUID>> cacheDylibs;
- std::set<std::pair<std::string, UUID>> 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;
-
- Architecture existingArch;
- (void)dyld_shared_cache_iterate(mappedCache, (uint32_t)statbuf.st_size,
- [&existingArch, &architecture](const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
- UUID uuid = *dylibInfo->uuid;
- DylibInfo info;
- info.uuid = uuid;
- existingArch.results.dylibs[ImageIdentifier(uuid)] = info;
- });
-
- return (existingArch == _configurations.find(configuration)->second.architectures.find(architecture)->second);
-}
-
-void Manifest::removeDylib(MachOProxy* proxy, const std::string& reason, const std::string& configuration,
- const std::string& architecture, std::unordered_set<ImageIdentifier>& processedIdentifiers)
-{
- 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->identifier) == 0) {
- archManifest.results.dylibs[proxy->identifier].uuid = proxy->uuid;
- archManifest.results.dylibs[proxy->identifier].installname = proxy->installName;
- processedIdentifiers.insert(proxy->identifier);
- }
- archManifest.results.exclude(MachOProxy::forIdentifier(proxy->identifier, architecture), reason);
-
- processedIdentifiers.insert(proxy->identifier);
-
- for (const auto& dependent : proxy->dependentIdentifiers) {
- auto dependentProxy = MachOProxy::forIdentifier(dependent, architecture);
- auto dependentResultIter = archManifest.results.dylibs.find(dependentProxy->identifier);
- if ( dependentProxy &&
- ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
- removeDylib(dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
- processedIdentifiers);
- }
- }
-}
-
-MachOProxy* Manifest::removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ) {
- std::set<ImageIdentifier> activeIdentifiers;
- 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 ) {
- activeIdentifiers.insert(dylibInfo.first);
- }
- }
- for (const auto& identifier : activeIdentifiers) {
- auto dylib = MachOProxy::forIdentifier(identifier, architecture);
- bool dependents = false;
- for (const auto& depedent : dylib->dependentIdentifiers) {
- if (depedent != identifier && activeIdentifiers.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<ImageIdentifier> empty;
- removeDylib( largestLeafDylib, "VM space overflow", configuration, architecture, empty );
- return largestLeafDylib;
-}
-
-void Manifest::calculateClosure( const std::string& configuration, const std::string& architecture ) {
- auto& archManifest = _configurations[configuration].architectures[architecture];
- std::unordered_set<ImageIdentifier> newIdentifiers;
-
- for ( auto& anchor : archManifest.anchors ) {
- newIdentifiers.insert(anchor.identifier);
- }
-
- std::unordered_set<ImageIdentifier> processedIdentifiers;
-
- while (!newIdentifiers.empty()) {
- std::unordered_set<ImageIdentifier> identifiersToProcess = newIdentifiers;
- newIdentifiers.clear();
-
- for (const auto& identifier : identifiersToProcess) {
- if (processedIdentifiers.count(identifier) > 0) {
- continue;
- }
-
- auto proxy = MachOProxy::forIdentifier(identifier, architecture);
-
- if (proxy == nullptr) {
- // No path
- continue;
- }
-
- // HACK: This is a policy decision we may want to revisit.
- if (!proxy->isDylib()) {
- continue;
- }
-
- if (!proxy->error.empty()) {
- archManifest.results.exclude(proxy, proxy->error);
- processedIdentifiers.insert(proxy->identifier);
- continue;
- }
-
- if (proxy->isDylib()) {
- if (_configurations[configuration].restrictedInstallnames.count(proxy->installName) != 0) {
- removeDylib(proxy, "Dylib '" + proxy->installName + "' removed due to explict restriction", configuration, architecture,
- processedIdentifiers);
- continue;
- }
-
- if (archManifest.results.dylibs.count(proxy->identifier) == 0) {
- archManifest.results.dylibs[proxy->identifier].uuid = proxy->uuid;
- archManifest.results.dylibs[proxy->identifier].installname = proxy->installName;
- archManifest.results.dylibs[proxy->identifier].included = true;
-
- processedIdentifiers.insert(proxy->identifier);
- }
- }
-
- for (const auto& dependency : proxy->requiredIdentifiers) {
- if (processedIdentifiers.count(dependency) == 0) {
- newIdentifiers.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"] = @(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.second.installname )] = 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 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 "MachOProxy.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.configuration(*configurations.begin()).architecture(architecture).results.dylibs;
-
- if ( _skipBuilds ) {
- log( "Build Skipped" );
-
- for ( auto& config : configurations ) {
- for ( auto& dylib : configResults ) {
- _manifest.configuration(config).architecture(architecture).results.exclude(MachOProxy::forIdentifier(dylib.first, architecture), "All dylibs excluded");
- }
- }
- return;
- }
-
- 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.configuration(config).architecture(architecture).results;
-
- for (auto& dylib : configResults) {
- if (dylib.second.included == false
- && results.dylibs.count(dylib.first)
- && results.dylibs[dylib.first].included == true) {
- results.exclude(MachOProxy::forIdentifier(dylib.first, architecture), 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.configuration(config).architecture(architecture).results.developmentCache.regions.push_back({ prot, vmAddr, vmAddr + size });
- } else {
- _manifest.configuration(config).architecture(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<MachOProxySegment>& 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.configuration(config).architecture(architecture).results.dylibForInstallname(installName).segments.push_back({ seg.name, vmAddr, vmAddr + seg.size });
- if (_manifest.configuration(config).architecture(architecture).results.dylibForInstallname(installName).segments.size() == 0) {
- warning("Attempting to write info for non-existent dylib");
- }
- }
- }
- });
- 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.configuration(config).architecture(architecture).results.developmentCache.cdHash = cache->cdHashString();
- } else {
- _manifest.configuration(config).architecture(architecture).results.productionCache.cdHash = cache->cdHashString();
- }
- }
-}
-
-void MultiCacheBuilder::buildCaches(std::string masterDstRoot) {
- if (_bniMode) {
- std::vector<std::set<std::string>> dedupedCacheSets;
- _manifest.forEachConfiguration([&dedupedCacheSets, this](const std::string& configName) {
- auto config = _manifest.configuration(configName);
- bool dupeFound = false;
-
- for (auto& cacheSet : dedupedCacheSets) {
- if (config == _manifest.configuration(*cacheSet.begin())) {
- cacheSet.insert(configName);
- dupeFound = true;
- break;
- }
- }
-
- if (!dupeFound) {
- std::set<std::string> temp;
- temp.insert(configName);
- 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.configuration(*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) {
- _manifest.forEachConfiguration([this, &masterDstRoot](const std::string& configName) {
- auto config = _manifest.configuration(configName);
- // for ( auto& configuration : _manifest.configurations ) {
- std::vector<std::string> prodBomPaths;
- std::vector<std::string> devBomPaths;
-
- for (auto& arch : config.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(), configName.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(), configName.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(), configName.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 {
- _manifest.runConcurrently(_buildQueue, _concurrencyLimitingSemaphore,
- [&](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>
-
-#include "MachOProxy.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<MachOProxySegment>&) {
- 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<MachOProxySegment>& segs) {
- for (const auto& 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<MachOProxySegment>&) {
- 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<MachOProxySegment>& 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 "MachOProxy.h"
-
-#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.configuration(configuration).architecture(architecture))
- , _buffer(nullptr)
- , _fileSize(0)
- , _vmSize(0)
- , _aliasCount(0)
- , _slideInfoFileOffset(0)
- , _slideInfoBufferSize(0)
-{
- auto maxCacheVMSize = sharedRegionRegionSize(_arch);
-
- for (auto& includedIdentifier : _archManifest.results.dylibs) {
- if (includedIdentifier.second.included) {
- //assert(manifest.dylibs.count(includedDylib.first) > 0);
- //assert(manifest.dylibs.find(includedDylib.first)->second.proxies.count(architecture) > 0);
- MachOProxy* proxy = MachOProxy::forIdentifier(includedIdentifier.first, architecture);
- assert(proxy != nullptr);
- assert(proxy->isDylib());
- _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<MachOProxySegment>& segments) {
- if ( !seenHeaders.count(machHeader) ) {
- seenHeaders.insert(machHeader);
-
- fprintf(fmap, "%s\n", installName);
- for (const 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];
- }
- }
- 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<MachOProxySegment> getSegments(const void* cacheBuffer, const void* machHeader)
-{
- std::vector<MachOProxySegment> 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;
- MachOProxySegment seg;
- seg.name = segCmd->segname();
- seg.name = segCmd->segname();
- seg.size = segCmd->vmsize();
- seg.vmaddr = segCmd->vmaddr();
- 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;
-
- // FIXME: Confirm these numbers for all platform/arch combos
- // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size
- if (_manifest.platform() == "osx") {
- _vmSize = _readOnlyRegion.address + (_readOnlyRegion.size * 9 / 10) - _textRegion.address;
- } else {
- _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.get());
- textImages->set_loadAddress(_segmentMap[dylib][0].address);
- textImages->set_textSegmentSize((uint32_t)_segmentMap[dylib].front().cacheSegSize);
- 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)
-{
- bindAllImagesInCache(_dylibs, _segmentMap, _pointersForASLR);
-}
-
-void SharedCache::writeCacheSegments(void)
-{
- uint8_t* cacheBytes = (uint8_t*)_buffer.get();
- for (auto& dylib : _dylibs) {
- const uint8_t* srcDylib = dylib->getBuffer();
-
- for (auto& seg : _segmentMap[dylib]) {
- 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[seg.base->fileOffset], 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 "mega-dylib-utils.h"
-
-#include "MultiCacheBuilder.h"
-
-#include "MachOProxy.h"
-#include "Manifest.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::string realPath(const std::string& path) {
- char resolvedPath[PATH_MAX];
- if (realpath( path.c_str(), &resolvedPath[0]) != nullptr) {
- return resolvedPath;
- } else {
- return "";
- }
-}
-
-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::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 = realPath(argv[++i]);
- processRoot(root, roots);
- } else if (strcmp(arg, "-copy_roots") == 0) {
- copyRoots = true;
- } else if (strcmp(arg, "-dylib_cache") == 0) {
- dylibCacheDir = realPath(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 = realPath(argv[++i]);
- } else if (strcmp(arg, "-release") == 0) {
- release = argv[++i];
- } else if (strcmp(arg, "-results") == 0) {
- resultPath = realPath(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) {
- manifest.forEachConfiguration([](const std::string& configName) {
- printf("%s\n", configName.c_str());
- });
- exit(0);
- }
-
- if (!manifest.filterForConfig(configuration)) {
- 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.calculateClosure(false);
- manifest.checkLinks();
-
- // 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) {
- manifest.forEachConfiguration([&manifest](const std::string& configName) {
- for (auto& arch : manifest.configuration(configName).architectures) {
- for (auto& dylib : arch.second.results.dylibs) {
- if (dylib.second.included) {
- MachOProxy* proxy = MachOProxy::forIdentifier(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 <array>
-#include <vector>
-#include <string>
-#include <algorithm>
-#include <unordered_set>
-#include <unordered_map>
-
-#ifndef UUID
-#include <uuid/uuid.h>
-
-struct UUID {
- UUID() {}
- UUID(const UUID& other) { uuid_copy(&_bytes[0], &other._bytes[0]); }
- UUID(const uuid_t other_uuid) { uuid_copy(&_bytes[0], other_uuid); }
- bool operator<(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) < 0; }
- bool operator==(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) == 0; }
- bool operator!=(const UUID& other) const { return !(*this == other); }
-
- size_t hash() const {
- size_t retval = 0;
- for (auto i = 0; i < 16/sizeof(size_t); ++i) {
- retval ^= ((size_t *)(&_bytes[0]))[i];
- }
- return retval;
- }
- const unsigned char* get() const { return &_bytes[0]; };
-private:
- std::array<unsigned char, 16> _bytes;
-};
-
-struct ImageIdentifier {
- ImageIdentifier() {}
- ImageIdentifier(const UUID &U) : _uuid(U) {}
- size_t hash() const { return _uuid.hash(); }
- bool operator<(const ImageIdentifier& other) const { return _uuid < other._uuid; }
- bool operator==(const ImageIdentifier& other) const { return _uuid == other._uuid; }
- bool operator!=(const ImageIdentifier& other) const { return !(*this == other); }
-
-private:
- UUID _uuid;
-};
-
-namespace std {
-template <>
-struct hash<UUID> {
- size_t operator()(const UUID& x) const
- {
- return x.hash();
- }
-};
-
-template <>
-struct hash<ImageIdentifier> {
- size_t operator()(const ImageIdentifier& x) const
- {
- return x.hash();
- }
-};
-}
-
-#endif
-
-#include "CacheFileAbstraction.hpp"
-
-#include "MachOFileAbstraction.hpp"
-
-#include "Manifest.h"
-
-struct MachOProxy;
-struct MachOProxySegment;
-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:
- std::tuple<uint8_t *, struct stat, bool> 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 MachOProxySegment* seg)
- : base(seg)
- , address(0)
- , cacheFileOffset(0)
- , cacheSegSize(0)
- {
- }
-
- const MachOProxySegment* 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<MachOProxySegment>& 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::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SegmentInfo>>& segmentMap, 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);
- 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::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 "mega-dylib-utils.h"
-
-#include "MultiCacheBuilder.h"
-#include "Manifest.h"
-#include "MachOProxy.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;
- MachOProxy::runOnAllProxies(false, [&](MachOProxy* proxy) {
- if (copied.count(proxy->path) > 0) {
- return;
- }
- if (!includeExecutables && !proxy->isDylib())
- return;
- (void)copyFile(proxy->buildPath, normalize_absolute_file_path(dylibCachePath + "/Root/" + proxy->path));
- copied.insert(proxy->path);
- });
-
- // HACK for 10.e
- (void)symlink("libstdc++.6.0.9.dylib", (dylibCachePath + "/Root/usr/lib/libstdc++.6.dylib").c_str());
- (void)symlink("libstdc++.6.0.9.dylib", (dylibCachePath + "/Root/usr/lib/libstdc++.dylib").c_str());
- log("Artifact dylibs copied");
-}
-
-void addArtifactPaths(Manifest &manifest) {
- manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
- manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
- manifest.setMetabomFile("./Metadata/metabom.bom");
-
- for (auto& projects : manifest.projects()) {
- manifest.addProjectSource(projects.first, "./Root", true);
- }
-}
-
-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.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);
- }
-
- auto dylibCacheCtx = std::make_shared<LoggingContext>("DylibCache");
- setLoggingContext(dylibCacheCtx);
-
- if (!skipWrites && !skipBuilds) {
- 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);
- manifest.checkLinks();
- 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 "mega-dylib-utils.h"
-#include "MultiCacheBuilder.h"
-#include "MachOProxy.h"
-#include "Manifest.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
-
-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);
-}
-
-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);
- }
-
- 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 manifest(archStrs, overlayPath, rootPath, paths);
-
- manifest.setPlatform(platform);
-
- // 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 );
-
- for (const std::string& archStr : archStrs) {
- std::string cachePath = cacheDir + "/dyld_shared_cache_" + archStr;
- if (!force && manifest.sameContentsAsCacheAtPath("localhost", archStr, cachePath)) {
- manifest.remove("localhost", archStr);
- verboseLog("%s is already up to date", cachePath.c_str());
- }
- }
-
- if (manifest.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();
-}
};
+
template <typename E>
class dyldCacheFileMapping {
public:
dyld_cache_accelerator_dof fields;
};
+
template <typename E>
class dyldCacheSlideInfo {
public:
+++ /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@
- */
-
-#ifndef __MACHO_BINDER__
-#define __MACHO_BINDER__
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <vector>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-#include "MachOLayout.hpp"
-#include "MachORebaser.hpp"
-#include "MachOTrie.hpp"
-
-#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
- #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
-#endif
-
-#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
- #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
-#endif
-
-
-template <typename A>
-class Binder : public Rebaser<A>
-{
-public:
- class CStringHash {
- public:
- size_t operator()(const char* __s) const {
- size_t __h = 0;
- for ( ; *__s; ++__s)
- __h = 5 * __h + *__s;
- return __h;
- };
- };
- struct CStringEquals {
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- typedef std::unordered_map<const char*, class Binder<A>*, CStringHash, CStringEquals> Map;
-
-
- Binder(const MachOLayoutAbstraction&);
- virtual ~Binder() {}
-
- const char* getDylibID() const;
- void setDependentBinders(const Map& map);
- void bind(std::vector<void*>&);
- void optimize();
- void addResolverClient(Binder<A>* clientDylib, const char* symbolName);
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
- struct BinderAndReExportFlag { Binder<A>* binder; bool reExport; };
- struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; };
- typedef std::unordered_map<const char*, pint_t, CStringHash, CStringEquals> NameToAddrMap;
- typedef std::unordered_set<const char*, CStringHash, CStringEquals> NameSet;
- typedef std::unordered_map<const char*, std::set<Binder<A>*>, CStringHash, CStringEquals> ResolverClientsMap;
-
-
- static bool isPublicLocation(const char* pth);
- void hoistPrivateRexports();
- int ordinalOfDependentBinder(Binder<A>* dep);
- void doBindDyldInfo(std::vector<void*>& pointersInData);
- void doBindDyldLazyInfo(std::vector<void*>& pointersInData);
- void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type,
- int libraryOrdinal, int64_t addend,
- const char* symbolName, bool lazyPointer, bool weakImport,
- std::vector<void*>& pointersInData);
- bool findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn,
- bool* isResolverSymbol, bool* isAbsolute);
- const char* parentUmbrella();
- pint_t runtimeAddressFromNList(const macho_nlist<P>* sym);
- void switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr);
- void switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
- pint_t findLazyPointerFor(const char* symbolName);
- void shareLazyPointersToResolvers();
-
-
- static uint8_t pointerRelocSize();
- static uint8_t pointerRelocType();
-
- std::vector<BinderAndReExportFlag> fDependentDylibs;
- NameToAddrMap fHashTable;
- NameSet fSymbolResolvers;
- NameSet fAbsoluteSymbols;
- std::vector<SymbolReExport> fReExportedSymbols;
- const macho_nlist<P>* fSymbolTable;
- const char* fStrings;
- const macho_dysymtab_command<P>* fDynamicInfo;
- const macho_segment_command<P>* fFristWritableSegment;
- const macho_dylib_command<P>* fDylibID;
- const macho_dylib_command<P>* fParentUmbrella;
- const macho_dyld_info_command<P>* fDyldInfo;
- bool fOriginallyPrebound;
- bool fReExportedSymbolsResolved;
- ResolverClientsMap fResolverInfo;
-};
-
-template <>
-uint32_t Binder<arm>::runtimeAddressFromNList(const macho_nlist<Pointer32<LittleEndian> >* sym)
-{
- if (sym->n_desc() & N_ARM_THUMB_DEF)
- return sym->n_value() + 1;
- else
- return sym->n_value();
-}
-
-template <typename A>
-typename A::P::uint_t Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* sym)
-{
- return sym->n_value();
-}
-
-
-template <typename A>
-Binder<A>::Binder(const MachOLayoutAbstraction& layout)
- : Rebaser<A>(layout),
- fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL),
- fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL),
- fParentUmbrella(NULL), fReExportedSymbolsResolved(false)
-{
- fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0);
- // update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit)
- ((macho_header<P>*)this->fHeader)->set_flags(this->fHeader->flags() | MH_PREBOUND | MH_SPLIT_SEGS | 0x80000000);
-
- // calculate fDynamicInfo, fStrings, fSymbolTable
- const macho_symtab_command<P>* symtab;
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = this->fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd()) {
- case LC_SYMTAB:
- symtab = (macho_symtab_command<P>*)cmd;
- fSymbolTable = (macho_nlist<P>*)(&this->fLinkEditBase[symtab->symoff()]);
- fStrings = (const char*)&this->fLinkEditBase[symtab->stroff()];
- break;
- case LC_DYSYMTAB:
- fDynamicInfo = (macho_dysymtab_command<P>*)cmd;
- break;
- case LC_ID_DYLIB:
- ((macho_dylib_command<P>*)cmd)->set_timestamp(0);
- fDylibID = (macho_dylib_command<P>*)cmd;
- break;
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB:
- ((macho_dylib_command<P>*)cmd)->set_timestamp(0);
- break;
- case LC_SUB_FRAMEWORK:
- fParentUmbrella = (macho_dylib_command<P>*)cmd;
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- fDyldInfo = (macho_dyld_info_command<P>*)cmd;
- break;
- case LC_RPATH:
- fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath());
- break;
- default:
- if ( cmd->cmd() & LC_REQ_DYLD )
- throwf("unknown required load command 0x%08X", cmd->cmd());
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
- if ( fDynamicInfo == NULL )
- throw "no LC_DYSYMTAB";
- if ( fSymbolTable == NULL )
- throw "no LC_SYMTAB";
- if ( fDyldInfo == NULL )
- throw "no LC_DYLD_INFO";
- // build hash table
- //fprintf(stderr, "exports for %s\n", layout.getFilePath());
- std::vector<mach_o::trie::Entry> exports;
- const uint8_t* exportsStart = layout.getDyldInfoExports();
- const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
- mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
- pint_t baseAddress = layout.getSegments()[0].newAddress();
- for(std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
- switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
- case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
- if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
- fSymbolResolvers.insert(it->name);
- }
- if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
- //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID());
- SymbolReExport sym;
- sym.exportName = it->name;
- sym.dylibOrdinal = it->other;
- sym.importName = it->importName;
- if ( (sym.importName == NULL) || (sym.importName[0] == '\0') )
- sym.importName = sym.exportName;
- fReExportedSymbols.push_back(sym);
- // fHashTable entry will be added in first call to findExportedSymbolAddress()
- }
- else {
- fHashTable[it->name] = it->address + baseAddress;
- }
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
- fHashTable[it->name] = it->address + baseAddress;
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
- fHashTable[it->name] = it->address;
- fAbsoluteSymbols.insert(it->name);
- break;
- default:
- throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath());
- break;
- }
- //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name);
- }
-}
-
-template <> uint8_t Binder<x86>::pointerRelocSize() { return 2; }
-template <> uint8_t Binder<x86_64>::pointerRelocSize() { return 3; }
-template <> uint8_t Binder<arm>::pointerRelocSize() { return 2; }
-template <> uint8_t Binder<arm64>::pointerRelocSize() { return 3; }
-
-template <> uint8_t Binder<x86>::pointerRelocType() { return GENERIC_RELOC_VANILLA; }
-template <> uint8_t Binder<x86_64>::pointerRelocType() { return X86_64_RELOC_UNSIGNED; }
-template <> uint8_t Binder<arm>::pointerRelocType() { return ARM_RELOC_VANILLA; }
-template <> uint8_t Binder<arm64>::pointerRelocType() { return ARM64_RELOC_UNSIGNED; }
-
-
-template <typename A>
-const char* Binder<A>::getDylibID() const
-{
- if ( fDylibID != NULL )
- return fDylibID->name();
- else
- return NULL;
-}
-
-template <typename A>
-const char* Binder<A>::parentUmbrella()
-{
- if ( fParentUmbrella != NULL )
- return fParentUmbrella->name();
- else
- return NULL;
-}
-
-
-template <typename A>
-bool Binder<A>::isPublicLocation(const char* pth)
-{
- // /usr/lib is a public location
- if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) )
- return true;
-
- // /System/Library/Frameworks/ is a public location
- if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) {
- const char* frameworkDot = strchr(&pth[27], '.');
- // but only top level framework
- // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
- // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
- // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
- // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
- if ( frameworkDot != NULL ) {
- int frameworkNameLen = frameworkDot - &pth[27];
- if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 )
- return true;
- }
- }
-
- return false;
-}
-
-template <typename A>
-void Binder<A>::setDependentBinders(const Map& map)
-{
- // build vector of dependent dylibs
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = this->fHeader->ncmds();
- const macho_load_command<P>* cmd = cmds;
- 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:
- const char* path = ((struct macho_dylib_command<P>*)cmd)->name();
- typename Map::const_iterator pos = map.find(path);
- if ( pos != map.end() ) {
- BinderAndReExportFlag entry;
- entry.binder = pos->second;
- entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB );
- fDependentDylibs.push_back(entry);
- }
- else {
- // the load command string does not match the install name of any loaded dylib
- // this could happen if there was not a world build and some dylib changed its
- // install path to be some symlinked path
-
- // use realpath() and walk map looking for a realpath match
- bool found = false;
- char targetPath[PATH_MAX];
- if ( realpath(path, targetPath) != NULL ) {
- for(typename Map::const_iterator it=map.begin(); it != map.end(); ++it) {
- char aPath[PATH_MAX];
- if ( realpath(it->first, aPath) != NULL ) {
- if ( strcmp(targetPath, aPath) == 0 ) {
- BinderAndReExportFlag entry;
- entry.binder = it->second;
- entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB );
- fDependentDylibs.push_back(entry);
- found = true;
- fprintf(stderr, "update_dyld_shared_cache: warning mismatched install path in %s for %s\n",
- this->getDylibID(), path);
- break;
- }
- }
- }
- }
- if ( ! found ) {
- if ( cmd->cmd() == LC_LOAD_WEAK_DYLIB ) {
- BinderAndReExportFlag entry;
- entry.binder = NULL;
- entry.reExport = false;
- fDependentDylibs.push_back(entry);
- break;
- }
- else {
- throwf("in %s can't find dylib %s", this->getDylibID(), path);
- }
- }
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-template <typename A>
-int Binder<A>::ordinalOfDependentBinder(Binder<A>* dep)
-{
- for (int i=0; i < fDependentDylibs.size(); ++i) {
- if ( fDependentDylibs[i].binder == dep )
- return i+1;
- }
- throw "dependend dylib not found";
-}
-
-template <typename A>
-void Binder<A>::hoistPrivateRexports()
-{
- std::vector<Binder<A>*> privateReExportedDylibs;
- for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
- if ( it->reExport && ! isPublicLocation(it->binder->getDylibID()) )
- privateReExportedDylibs.push_back(it->binder);
- }
- if ( privateReExportedDylibs.size() != 0 ) {
- // parse export info into vector of exports
- const uint8_t* exportsStart = this->fLayout.getDyldInfoExports();
- const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
- std::vector<mach_o::trie::Entry> exports;
- mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
- //fprintf(stderr, "%s exports %lu symbols from trie of size %u \n", this->fLayout.getFilePath(), exports.size(), fDyldInfo->export_size());
-
- // add re-exports for each export from an re-exported dylib
- for(typename std::vector<Binder<A>*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) {
- Binder<A>* binder = *it;
- int ordinal = ordinalOfDependentBinder(binder);
- const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports();
- const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()];
- std::vector<mach_o::trie::Entry> aDylibsExports;
- mach_o::trie::parseTrie(aDylibsExportsStart, aDylibsExportsEnd, aDylibsExports);
- //fprintf(stderr, "%s re-exports %lu symbols from %s\n", this->fLayout.getFilePath(), aDylibsExports.size(), binder->getDylibID());
- for(std::vector<mach_o::trie::Entry>::iterator eit = aDylibsExports.begin(); eit != aDylibsExports.end(); ++eit) {
- mach_o::trie::Entry entry = *eit;
- entry.flags |= EXPORT_SYMBOL_FLAGS_REEXPORT;
- entry.other = ordinal;
- entry.importName = NULL;
- exports.push_back(entry);
- }
- }
- // rebuild new combined trie
- std::vector<uint8_t> newExportTrieBytes;
- newExportTrieBytes.reserve(fDyldInfo->export_size());
- mach_o::trie::makeTrie(exports, newExportTrieBytes);
- //fprintf(stderr, "%s now exports %lu symbols from trie of size %lu\n", this->fLayout.getFilePath(), exports.size(), newExportTrieBytes.size());
-
- // allocate new buffer and set export_off to use new buffer instead
- uint32_t newExportsSize = newExportTrieBytes.size();
- uint8_t* sideTrie = new uint8_t[newExportsSize];
- memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
- this->fLayout.setDyldInfoExports(sideTrie);
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
- }
-}
-
-
-template <typename A>
-void Binder<A>::bind(std::vector<void*>& pointersInData)
-{
- this->doBindDyldInfo(pointersInData);
- this->doBindDyldLazyInfo(pointersInData);
- // weak bind info is processed at launch time
-}
-
-template <typename A>
-void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
- int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector<void*>& pointersInData)
-{
- //printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
- const std::vector<MachOLayoutAbstraction::Segment>& segments = this->fLayout.getSegments();
- if ( segmentIndex > segments.size() )
- throw "bad segment index in rebase info";
-
- if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP )
- throw "dynamic lookup linkage not allowed in dyld shared cache";
-
- if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE )
- throw "linkage to main executable not allowed in dyld shared cache";
-
- if ( libraryOrdinal < 0 )
- throw "bad mach-o binary, special library ordinal not allowd in dyld shared cache";
-
- if ( (unsigned)libraryOrdinal > fDependentDylibs.size() )
- throw "bad mach-o binary, library ordinal too big";
-
- Binder<A>* binder;
- if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF )
- binder = this;
- else
- binder = fDependentDylibs[libraryOrdinal-1].binder;
- pint_t targetSymbolAddress;
- bool isResolverSymbol = false;
- bool isAbsolute = false;
- Binder<A>* foundIn;
- if ( weakImport && (binder == NULL) ) {
- targetSymbolAddress = 0;
- foundIn = NULL;
- }
- else {
- if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) )
- throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID());
- }
-
- // don't bind lazy pointers to resolver stubs in shared cache
- if ( lazyPointer && isResolverSymbol ) {
- if ( foundIn != this ) {
- // record that this dylib has a lazy pointer to a resolver function
- foundIn->addResolverClient(this, symbolName);
- // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID());
- }
- return;
- }
-
- // do actual update
- const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex];
- uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segmentOffset;
- pint_t* mappedAddrP = (pint_t*)mappedAddr;
- uint32_t* mappedAddr32 = (uint32_t*)mappedAddr;
- int32_t svalue32new;
- switch ( type ) {
- case BIND_TYPE_POINTER:
- P::setP(*mappedAddrP, targetSymbolAddress + addend);
- break;
-
- case BIND_TYPE_TEXT_ABSOLUTE32:
- E::set32(*mappedAddr32, targetSymbolAddress + addend);
- break;
-
- case BIND_TYPE_TEXT_PCREL32:
- svalue32new = seg.address() + segmentOffset + 4 - (targetSymbolAddress + addend);
- E::set32(*mappedAddr32, svalue32new);
- break;
-
- default:
- throw "bad bind type";
- }
- if ( !isAbsolute )
- pointersInData.push_back(mappedAddr);
-}
-
-
-
-template <typename A>
-void Binder<A>::doBindDyldLazyInfo(std::vector<void*>& pointersInData)
-{
- const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()];
- const uint8_t* end = &p[fDyldInfo->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 = 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:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, pointersInData);
- 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:
- throwf("bad lazy bind opcode %d", *p);
- }
- }
-
-
-}
-
-template <typename A>
-void Binder<A>::doBindDyldInfo(std::vector<void*>& pointersInData)
-{
- const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()];
- const uint8_t* end = &p[fDyldInfo->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;
- uint32_t count;
- uint32_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 = 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:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
- segmentOffset += sizeof(pint_t);
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
- segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
- 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) {
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData);
- segmentOffset += skip + sizeof(pint_t);
- }
- break;
- default:
- throwf("bad bind opcode %d", *p);
- }
- }
-}
-
-template <typename A>
-bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
-{
- *foundIn = NULL;
- // since re-export chains can be any length, re-exports cannot be resolved in setDependencies()
- // instead we lazily, recursively update
- if ( !fReExportedSymbolsResolved ) {
-
- // update fHashTable with any individual symbol re-exports
- for (typename std::vector<SymbolReExport>::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) {
- pint_t targetSymbolAddress;
- bool isResolver;
- bool isAb;
-
- if ( it->dylibOrdinal <= 0 )
- throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache";
-
- Binder<A>* binder = fDependentDylibs[it->dylibOrdinal-1].binder;
-
- if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver, &isAb) )
- throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID());
-
- if ( isResolver )
- fSymbolResolvers.insert(name);
-
- fHashTable[it->exportName] = targetSymbolAddress;
- }
- // mark as done
- fReExportedSymbolsResolved = true;
- }
-
- *isResolverSymbol = false;
- if ( !fSymbolResolvers.empty() && fSymbolResolvers.count(name) ) {
- // lazy pointers should be left unbound, rather than bind to resolver stub
- *isResolverSymbol = true;
- }
-
- // search this dylib
- typename NameToAddrMap::iterator pos = fHashTable.find(name);
- if ( pos != fHashTable.end() ) {
- *result = pos->second;
- //fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID());
- *foundIn = this;
- *isAbsolute = (fAbsoluteSymbols.count(name) != 0);
- return true;
- }
-
- // search re-exported dylibs
- for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
- if ( it->reExport ) {
- if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol, isAbsolute) )
- return true;
- }
- }
- //fprintf(stderr, "findExportedSymbolAddress(%s) => not found in %s\n", name, this->getDylibID());
- return false;
-}
-
-// record which dylibs will be using this dylibs lazy pointer
-template <typename A>
-void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName)
-{
- fResolverInfo[symbolName].insert(clientDylib);
-}
-
-
-template <typename A>
-typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
-{
- static const bool log = false;
-
- // lookup in lazy pointer section
- const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = this->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 ) {
- const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- 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 = sect->size() / sizeof(pint_t);
- const uint32_t indirectTableOffset = sect->reserved1();
- pint_t vmlocation = 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 = &fSymbolTable[symbolIndex];
- const char* aName = &fStrings[aSymbol->n_strx()];
- //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]);
- if ( strcmp(aName, symbolName) == 0 ) {
- if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID());
- return vmlocation;
- }
- break;
- }
- }
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking for re-export symbol\n", symbolName, this->getDylibID());
- for (typename std::vector<SymbolReExport>::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) {
- if ( strcmp(it->exportName, symbolName) != 0 )
- continue;
-
- if ( it->dylibOrdinal <= 0 )
- throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache";
-
- Binder<A>* binder = fDependentDylibs[it->dylibOrdinal-1].binder;
- return binder->findLazyPointerFor(it->importName);
- }
-
- if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking re-export dylibs\n", symbolName, this->getDylibID());
- for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
- if ( it->reExport ) {
- pint_t result = it->binder->findLazyPointerFor(symbolName);
- if ( result != 0 )
- return result;
- }
- }
-
- if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID());
- return 0;
-}
-
-// called after all binding is done to optimize lazy pointers
-template <typename A>
-void Binder<A>::optimize()
-{
- hoistPrivateRexports();
- shareLazyPointersToResolvers();
-}
-
-template <typename A>
-void Binder<A>::shareLazyPointersToResolvers()
-{
- for (const auto &entry : fResolverInfo) {
- const char* resolverSymbolName = entry.first;
- if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) {
- for (Binder<A>* clientDylib : entry.second) {
- clientDylib->switchStubToUseSharedLazyPointer(resolverSymbolName, lpVMAddr);
- }
- }
- else {
- fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", resolverSymbolName, this->getDylibID());
- }
- }
-}
-
-
-
-template <>
-void Binder<arm>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
-{
- if ( stubSize != 16 ) {
- fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID());
- return;
- }
- uint32_t* instructions = (uint32_t*)stubMappedAddress;
- if ( (E::get32(instructions[0]) != 0xe59fc004)
- || (E::get32(instructions[1]) != 0xe08fc00c)
- || (E::get32(instructions[2]) != 0xe59cf000)
- ) {
- fprintf(stderr, "could not optimize ARM stub to resolver function in %s because instructions are not as expected\n", this->getDylibID());
- return;
- }
- // last .long in stub is: lazyPtr - (stub+8)
- // alter to point to more optimal lazy pointer
- uint32_t betterOffset = lpVMAddr - (stubVMAddress + 12);
- E::set32(instructions[3], betterOffset);
-}
-
-
-template <>
-void Binder<x86_64>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
-{
- if ( stubSize != 6 ) {
- fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID());
- return;
- }
- if ( (stubMappedAddress[0] != 0xFF) || (stubMappedAddress[1] != 0x25) ) {
- fprintf(stderr, "could not optimize stub to resolver function in %s because instructions are not as expected\n", this->getDylibID());
- return;
- }
- // last four bytes in stub is RIP relative offset to lazy pointer
- // alter to point to more optimal lazy pointer
- uint32_t betterOffset = lpVMAddr - (stubVMAddress + 6);
- E::set32(*((uint32_t*)(&stubMappedAddress[2])), betterOffset);
-}
-
-template <typename A>
-void Binder<A>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress)
-{
- // Remaining architectures are not optimized
- //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress);
-}
-
-// search for stub in this image that call target symbol name and then optimize its lazy pointer
-template <typename A>
-void Binder<A>::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr)
-{
- // find named stub
- const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
- const uint32_t cmd_count = this->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;
- 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 = sect->addr();
- uint8_t* stubsMappingStart = (uint8_t*)this->mappedAddressForNewAddress(stubsVMStart);
- const uint32_t indirectTableOffset = sect->reserved1();
- const uint32_t stubSize = sect->reserved2();
- uint32_t elementCount = 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>* sym = &this->fSymbolTable[symbolIndex];
- const char* symName = &fStrings[sym->n_strx()];
- if ( strcmp(symName, stubName) == 0 )
- this->switchStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
- }
- break;
- }
- }
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-
-#endif // __MACHO_BINDER__
-
-
-
-
#define MH_HAS_OBJC 0x40000000
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
+
#include "FileAbstraction.hpp"
#include "Architectures.hpp"
if (p == end)
throw "malformed sleb128";
byte = *p++;
- result |= ((byte & 0x7f) << bit);
+ result |= (((int64_t)(byte & 0x7f)) << bit);
bit += 7;
} while (byte & 0x80);
// sign extend negative numbers
+++ /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@
- */
-
-#ifndef __MACHO_LAYOUT__
-#define __MACHO_LAYOUT__
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/errno.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <vector>
-#include <set>
-#include <unordered_map>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-
-
-void throwf(const char* format, ...) __attribute__((format(printf, 1, 2)));
-
-__attribute__((noreturn))
-void throwf(const char* format, ...)
-{
- va_list list;
- char* p;
- va_start(list, format);
- vasprintf(&p, format, list);
- va_end(list);
-
- const char* t = p;
- throw t;
-}
-
-
-class MachOLayoutAbstraction
-{
-public:
- struct Segment
- {
- public:
- Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t sectionsSize,
- uint64_t sectionsAlignment, uint64_t align,
- uint32_t prot, uint32_t sectionCount, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
- fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot),
- fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align),
- fPermissions(prot), fSectionCount(sectionCount), fSectionsSize(sectionsSize),
- fSectionsAlignment(sectionsAlignment), fNewAddress(0), fMappedAddress(NULL) {
- strlcpy(fOrigName, segName, 16);
- }
-
- uint64_t address() const { return fOrigAddress; }
- uint64_t size() const { return fSize; }
- uint64_t fileOffset() const { return fFileOffset; }
- uint64_t fileSize() const { return fFileSize; }
- uint32_t permissions() const { return fPermissions; }
- bool readable() const { return fPermissions & VM_PROT_READ; }
- bool writable() const { return fPermissions & VM_PROT_WRITE; }
- bool executable() const { return fPermissions & VM_PROT_EXECUTE; }
- uint64_t alignment() const { return fAlignment; }
- const char* name() const { return fOrigName; }
- uint64_t newAddress() const { return fNewAddress; }
- uint32_t sectionCount() const{ return fSectionCount; }
- uint64_t sectionsSize() const{ return fSectionsSize; }
- uint64_t sectionsAlignment() const { return fSectionsAlignment; }
- void* mappedAddress() const { return fMappedAddress; }
- void setNewAddress(uint64_t addr) { fNewAddress = addr; }
- void setMappedAddress(void* addr) { fMappedAddress = addr; }
- void setSize(uint64_t new_size) { fSize = new_size; }
- void setFileOffset(uint64_t new_off) { fFileOffset = new_off; }
- void setFileSize(uint64_t new_size) { fFileSize = new_size; }
- void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; }
- void setSectionsAlignment(uint64_t v){ fSectionsAlignment = v; }
- void reset() { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; }
- private:
- uint64_t fOrigAddress;
- uint64_t fOrigSize;
- uint64_t fOrigFileOffset;
- uint64_t fOrigFileSize;
- uint32_t fOrigPermissions;
- char fOrigName[16];
- uint64_t fSize;
- uint64_t fFileOffset;
- uint64_t fFileSize;
- uint64_t fAlignment;
- uint32_t fPermissions;
- uint32_t fSectionCount;
- uint64_t fSectionsSize;
- uint64_t fSectionsAlignment;
- uint64_t fNewAddress;
- void* fMappedAddress;
- };
-
- struct Library
- {
- const char* name;
- uint32_t currentVersion;
- uint32_t compatibilityVersion;
- bool weakImport;
- };
-
-
- virtual ArchPair getArchPair() const = 0;
- virtual const char* getFilePath() const = 0;
- virtual uint64_t getOffsetInUniversalFile() const = 0;
- virtual uint32_t getFileType() const = 0;
- virtual uint32_t getFlags() const = 0;
- virtual Library getID() const = 0;
- virtual bool isDylib() const = 0;
- virtual bool isSplitSeg() const = 0;
- virtual bool hasSplitSegInfo() const = 0;
- virtual bool hasSplitSegInfoV2() const = 0;
- virtual bool inSharableLocation() const = 0;
- virtual bool hasDynamicLookupLinkage() const = 0;
- virtual bool hasMainExecutableLookupLinkage() const = 0;
- virtual bool isTwoLevelNamespace() const = 0;
- virtual bool hasDyldInfo() const = 0;
- virtual bool hasMultipleReadWriteSegments() const = 0;
- virtual uint32_t getNameFileOffset() const = 0;
- virtual time_t getLastModTime() const = 0;
- virtual ino_t getInode() const = 0;
- virtual std::vector<Segment>& getSegments() = 0;
- virtual const std::vector<Segment>& getSegments() const = 0;
- virtual const Segment* getSegment(const char* name) const = 0;
- virtual const std::vector<Library>& getLibraries() const = 0;
- virtual uint64_t getBaseAddress() const = 0;
- virtual uint64_t getVMSize() const = 0;
- virtual uint64_t getBaseExecutableAddress() const = 0;
- virtual uint64_t getBaseWritableAddress() const = 0;
- virtual uint64_t getBaseReadOnlyAddress() const = 0;
- virtual uint64_t getExecutableVMSize() const = 0;
- virtual uint64_t getWritableVMSize() const = 0;
- virtual uint64_t getReadOnlyVMSize() const = 0;
- // need getDyldInfoExports because export info uses ULEB encoding and size could grow
- virtual const uint8_t* getDyldInfoExports() const = 0;
- virtual void setDyldInfoExports(const uint8_t* newExports) const = 0;
- virtual void uuid(uuid_t u) const = 0;
-};
-
-
-
-
-template <typename A>
-class MachOLayout : public MachOLayoutAbstraction
-{
-public:
- MachOLayout(const void* machHeader, uint64_t offset, const char* path,
- ino_t inode, time_t modTime, uid_t uid);
- virtual ~MachOLayout() {}
-
- virtual ArchPair getArchPair() const { return fArchPair; }
- virtual const char* getFilePath() const { return fPath; }
- virtual uint64_t getOffsetInUniversalFile() const { return fOffset; }
- virtual uint32_t getFileType() const { return fFileType; }
- virtual uint32_t getFlags() const { return fFlags; }
- virtual Library getID() const { return fDylibID; }
- virtual bool isDylib() const { return fIsDylib; }
- virtual bool isSplitSeg() const;
- virtual bool hasSplitSegInfo() const { return fSplitSegInfo != NULL; }
- virtual bool hasSplitSegInfoV2() const{ return fHasSplitSegInfoV2; }
- virtual bool inSharableLocation() const { return fShareableLocation; }
- virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
- virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
- virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); }
- virtual bool hasDyldInfo() const { return fHasDyldInfo; }
- virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; }
- virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; }
- virtual time_t getLastModTime() const { return fMTime; }
- virtual ino_t getInode() const { return fInode; }
- virtual std::vector<Segment>& getSegments() { return fSegments; }
- virtual const std::vector<Segment>& getSegments() const { return fSegments; }
- virtual const Segment* getSegment(const char* name) const;
- virtual const std::vector<Library>& getLibraries() const { return fLibraries; }
- virtual uint64_t getBaseAddress() const { return fLowSegment->address(); }
- virtual uint64_t getVMSize() const { return fVMSize; }
- virtual uint64_t getBaseExecutableAddress() const { return fLowExecutableSegment->address(); }
- virtual uint64_t getBaseWritableAddress() const { return fLowWritableSegment->address(); }
- virtual uint64_t getBaseReadOnlyAddress() const { return fLowReadOnlySegment->address(); }
- virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; }
- virtual uint64_t getWritableVMSize() const { return fVMWritablSize; }
- virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; }
- virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; }
- virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; }
- virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); }
-
-private:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
- uint64_t segmentSize(const macho_segment_command<typename A::P>* segCmd) const;
- uint64_t segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
- uint64_t segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
- uint64_t sectionsSize(const macho_segment_command<typename A::P>* segCmd) const;
- uint64_t sectionsAlignment(const macho_segment_command<typename A::P>* segCmd) const;
-
- bool validReadWriteSeg(const Segment& seg) const;
-
- static cpu_type_t arch();
-
- const char* fPath;
- uint64_t fOffset;
- uint32_t fFileType;
- ArchPair fArchPair;
- uint32_t fFlags;
- std::vector<Segment> fSegments;
- std::vector<Library> fLibraries;
- const Segment* fLowSegment;
- const Segment* fLowExecutableSegment;
- const Segment* fLowWritableSegment;
- const Segment* fLowReadOnlySegment;
- Library fDylibID;
- uint32_t fNameFileOffset;
- time_t fMTime;
- ino_t fInode;
- uint64_t fVMSize;
- uint64_t fVMExecutableSize;
- uint64_t fVMWritablSize;
- uint64_t fVMReadOnlySize;
- const macho_linkedit_data_command<P>* fSplitSegInfo;
- bool fHasSplitSegInfoV2;
- bool fShareableLocation;
- bool fDynamicLookupLinkage;
- bool fMainExecutableLookupLinkage;
- bool fIsDylib;
- bool fHasDyldInfo;
- bool fHasTooManyWritableSegments;
- mutable const uint8_t* fDyldInfoExports;
- uuid_t fUUID;
-};
-
-
-
-class UniversalMachOLayout
-{
-public:
- UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
- ~UniversalMachOLayout() {}
-
- static const UniversalMachOLayout& find(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
- const MachOLayoutAbstraction* getSlice(ArchPair ap) const;
- const std::vector<MachOLayoutAbstraction*>& allLayouts() const { return fLayouts; }
-
-private:
- class CStringHash {
- public:
- size_t operator()(const char* __s) const {
- size_t __h = 0;
- for ( ; *__s; ++__s)
- __h = 5 * __h + *__s;
- return __h;
- };
- };
- struct CStringEquals {
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- typedef std::unordered_map<const char*, const UniversalMachOLayout*, CStringHash, CStringEquals> PathToNode;
-
- static bool requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
-
- static PathToNode fgLayoutCache;
- const char* fPath;
- std::vector<MachOLayoutAbstraction*> fLayouts;
-};
-
-UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache;
-
-
-
-
-const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const
-{
- // use matching cputype and cpusubtype
- for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
- const MachOLayoutAbstraction* layout = *it;
- if ( layout->getArchPair().arch == ap.arch ) {
- switch ( ap.arch ) {
- case CPU_TYPE_ARM:
- case CPU_TYPE_X86_64:
- if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) )
- return layout;
- break;
- default:
- return layout;
- }
- }
- }
- // if requesting x86_64h and it did not exist, try x86_64 as a fallback
- if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) {
- ap.subtype = CPU_SUBTYPE_X86_64_ALL;
- return this->getSlice(ap);
- }
- return NULL;
-}
-
-
-const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const std::set<ArchPair>* onlyArchs)
-{
- // look in cache
- PathToNode::iterator pos = fgLayoutCache.find(path);
- if ( pos != fgLayoutCache.end() )
- return *pos->second;
-
- // create UniversalMachOLayout
- const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs);
-
- // add it to cache
- fgLayoutCache[result->fPath] = result;
-
- return *result;
-}
-
-
-bool UniversalMachOLayout::requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
-{
- if ( onlyArchs == NULL )
- return true;
- // must match cputype and cpusubtype
- for (std::set<ArchPair>::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) {
- ArchPair anArch = *it;
- if ( cpuType == anArch.arch ) {
- switch ( cpuType ) {
- case CPU_TYPE_ARM:
- if ( cpuSubType == anArch.subtype )
- return true;
- break;
- default:
- return true;
- }
- }
- }
- return false;
-}
-
-
-UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set<ArchPair>* onlyArchs)
- : fPath(strdup(path))
-{
- // map in whole file
- int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 ) {
- int err = errno;
- if ( err == ENOENT )
- throwf("file not found");
- else
- throwf("can't open file, errno=%d", err);
- }
- struct stat stat_buf;
- if ( fstat(fd, &stat_buf) == -1)
- throwf("can't stat open file %s, errno=%d", path, errno);
- if ( stat_buf.st_size < 20 )
- throwf("file too small %s", path);
- uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- if ( p == (uint8_t*)(-1) )
- throwf("can't map file %s, errno=%d", path, errno);
- ::close(fd);
-
- try {
- // if fat file, process each architecture
- const fat_header* fh = (fat_header*)p;
- const mach_header* mh = (mach_header*)p;
- if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- // Fat header is always big-endian
- const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header));
- const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) {
- uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset);
- if ( fileOffset > stat_buf.st_size ) {
- throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
- i, OSSwapBigToHostInt32(slices[i].cputype), path);
- }
- if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) {
- throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
- i, OSSwapBigToHostInt32(slices[i].cputype), path);
- }
- try {
- switch ( OSSwapBigToHostInt32(slices[i].cputype) ) {
- case CPU_TYPE_I386:
- fLayouts.push_back(new MachOLayout<x86>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- break;
- case CPU_TYPE_X86_64:
- fLayouts.push_back(new MachOLayout<x86_64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- break;
- case CPU_TYPE_ARM:
- fLayouts.push_back(new MachOLayout<arm>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- break;
- case CPU_TYPE_ARM64:
- fLayouts.push_back(new MachOLayout<arm64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- break;
- default:
- throw "unknown slice in fat file";
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "warning: %s for %s\n", msg, path);
- }
- }
- }
- }
- else {
- try {
- if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
- if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
- fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
- if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
- fLayouts.push_back(new MachOLayout<x86_64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
- if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
- fLayouts.push_back(new MachOLayout<arm>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) {
- if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
- fLayouts.push_back(new MachOLayout<arm64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- }
- else {
- throw "unknown file format";
- }
- }
- catch (const char* msg) {
- fprintf(stderr, "warning: %s for %s\n", msg, path);
- }
- }
- }
- catch (...) {
- ::munmap(p, stat_buf.st_size);
- throw;
- }
-}
-
-
-template <typename A>
-uint64_t MachOLayout<A>::segmentSize(const macho_segment_command<typename A::P>* segCmd) const
-{
- // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
- if ( segCmd->nsects() > 0 ) {
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
- const macho_section<P>* const lastSection = §ionsStart[segCmd->nsects()-1];
- uint64_t endSectAddr = lastSection->addr() + lastSection->size();
- uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096);
- if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) {
- uint64_t size = endSectAddrPage - segCmd->vmaddr();
- //if ( size != segCmd->vmsize() )
- // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n",
- // segCmd->segname(), size, segCmd->vmsize(), getFilePath());
- return size;
- }
- }
- return segCmd->vmsize();
-}
-
-template <typename A>
-uint64_t MachOLayout<A>::segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const
-{
- int p2align = 12;
- if ( segCmd->nsects() > 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()-1];
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( sect->align() > p2align )
- p2align = sect->align();
- }
- }
- return (1 << p2align);
-}
-
-template <typename A>
-uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const
-{
- // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
- if ( segCmd->nsects() > 0 ) {
- uint64_t endOffset = segCmd->fileoff();
- 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->offset() != 0 )
- endOffset = sect->offset() + sect->size();
- }
- uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096);
- //if ( size != segCmd->filesize() )
- // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n",
- // segCmd->segname(), size, segCmd->filesize(), getFilePath());
- return size;
- }
- return segCmd->filesize();
-}
-
-template <typename A>
-uint64_t MachOLayout<A>::sectionsSize(const macho_segment_command<typename A::P>* segCmd) const
-{
- if ( segCmd->nsects() > 0 ) {
- const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
- const macho_section<P>* const lastSection = §ionsStart[segCmd->nsects()-1];
- uint64_t endSectAddr = lastSection->addr() + lastSection->size();
- if ( endSectAddr < (segCmd->vmaddr() + segCmd->vmsize()) ) {
- uint64_t size = endSectAddr - segCmd->vmaddr();
- return size;
- }
- }
- return segCmd->vmsize();
-}
-
-template <typename A>
-uint64_t MachOLayout<A>::sectionsAlignment(const macho_segment_command<typename A::P>* segCmd) const
-{
- int p2align = 4;
- if ( hasSplitSegInfoV2() && (segCmd->nsects() > 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()-1];
- for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- if ( sect->align() > p2align )
- p2align = sect->align();
- }
- }
- return (1 << p2align);
-}
-
-
-
-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),
- fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
- fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
-{
- fDylibID.name = NULL;
- fDylibID.currentVersion = 0;
- fDylibID.compatibilityVersion = 0;
- bzero(fUUID, sizeof(fUUID));
-
- const macho_header<P>* mh = (const macho_header<P>*)machHeader;
- if ( mh->cputype() != arch() )
- throw "Layout object is wrong architecture";
- switch ( mh->filetype() ) {
- case MH_DYLIB:
- fIsDylib = true;
- break;
- case MH_BUNDLE:
- case MH_EXECUTE:
- case MH_DYLIB_STUB:
- case MH_DYLINKER:
- break;
- default:
- throw "file is not a mach-o final linked image";
- }
- fFlags = mh->flags();
- fFileType = mh->filetype();
- fArchPair.arch = mh->cputype();
- fArchPair.subtype = mh->cpusubtype();
-
- const macho_dyld_info_command<P>* dyldInfo = NULL;
- const macho_symtab_command<P>* symbolTableCmd = NULL;
- const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
- 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) {
- switch ( cmd->cmd() ) {
- case LC_ID_DYLIB:
- {
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- fDylibID.name = strdup(dylib->name());
- fDylibID.currentVersion = dylib->current_version();
- fDylibID.compatibilityVersion = dylib->compatibility_version();
- fNameFileOffset = dylib->name() - (char*)machHeader;
- fShareableLocation = ( (strncmp(fDylibID.name, "/usr/lib/", 9) == 0) || (strncmp(fDylibID.name, "/System/Library/", 16) == 0) );
- }
- 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;
- Library lib;
- lib.name = strdup(dylib->name());
- lib.currentVersion = dylib->current_version();
- lib.compatibilityVersion = dylib->compatibility_version();
- lib.weakImport = ( cmd->cmd() == LC_LOAD_WEAK_DYLIB );
- fLibraries.push_back(lib);
- }
- break;
- case LC_SEGMENT_SPLIT_INFO:
- fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd;
- break;
- case macho_segment_command<P>::CMD:
- {
- const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(),
- segmentFileSize(segCmd), sectionsSize(segCmd), sectionsAlignment(segCmd),
- segmentAlignment(segCmd), segCmd->initprot(),
- segCmd->nsects(), segCmd->segname()));
- }
- break;
- case LC_SYMTAB:
- symbolTableCmd = (macho_symtab_command<P>*)cmd;
- break;
- case LC_DYSYMTAB:
- dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- fHasDyldInfo = true;
- dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
- break;
- case LC_UUID:
- {
- const macho_uuid_command<P>* uc = (macho_uuid_command<P>*)cmd;
- memcpy(&fUUID, uc->uuid(), 16);
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- fLowSegment = NULL;
- fLowExecutableSegment = NULL;
- fLowWritableSegment = NULL;
- fLowReadOnlySegment = NULL;
- fVMExecutableSize = 0;
- fVMWritablSize = 0;
- fVMReadOnlySize = 0;
- fVMSize = 0;
- const Segment* highSegment = NULL;
- for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
- const Segment& seg = *it;
- if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) )
- fLowSegment = &seg;
- if ( (highSegment == NULL) || (seg.address() > highSegment->address()) )
- highSegment = &seg;
- if ( seg.executable() ) {
- if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) )
- fLowExecutableSegment = &seg;
- fVMExecutableSize += seg.size();
- }
- else if ( seg.writable()) {
- if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) )
- fLowWritableSegment = &seg;
- fVMWritablSize += seg.size();
- if ( !validReadWriteSeg(seg) ) {
- fHasTooManyWritableSegments = true;
- }
- }
- else {
- if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
- fLowReadOnlySegment = &seg;
- fVMReadOnlySize += seg.size();
- }
- }
- if ( (highSegment != NULL) && (fLowSegment != NULL) )
- fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
-
- // scan undefines looking, for magic ordinals
- if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) {
- const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)machHeader + symbolTableCmd->symoff());
- const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym();
- const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym();
- for (uint32_t i=startUndefs; i < endUndefs; ++i) {
- uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
- if ( ordinal == DYNAMIC_LOOKUP_ORDINAL )
- fDynamicLookupLinkage = true;
- else if ( ordinal == EXECUTABLE_ORDINAL )
- fMainExecutableLookupLinkage = true;
- }
- }
-
- if ( dyldInfo != NULL ) {
- if ( dyldInfo->export_off() != 0 ) {
- fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off();
- }
- }
-
- if ( fSplitSegInfo != NULL ) {
- const uint8_t* infoStart = (uint8_t*)machHeader + fSplitSegInfo->dataoff();
- fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT );
- if ( !fHasSplitSegInfoV2 ) {
- // split seg version not known when segments created
- // v1 does not support packing, so simulate that by forcing alignment
- for (Segment& seg : fSegments) {
- seg.setSectionsAlignment(4096);
- }
- }
- }
-
-}
-
-template <> cpu_type_t MachOLayout<x86>::arch() { return CPU_TYPE_I386; }
-template <> cpu_type_t MachOLayout<x86_64>::arch() { return CPU_TYPE_X86_64; }
-template <> cpu_type_t MachOLayout<arm>::arch() { return CPU_TYPE_ARM; }
-template <> cpu_type_t MachOLayout<arm64>::arch() { return CPU_TYPE_ARM64; }
-
-template <>
-bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const
-{
- return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0);
-}
-
-template <typename A>
-bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const
-{
- return (strcmp(seg.name(), "__DATA") == 0);
-}
-
-
-template <>
-bool MachOLayout<x86>::isSplitSeg() const
-{
- return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
-}
-
-template <>
-bool MachOLayout<arm>::isSplitSeg() const
-{
- return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
-}
-
-template <typename A>
-bool MachOLayout<A>::isSplitSeg() const
-{
- return false;
-}
-
-template <typename A>
-const MachOLayoutAbstraction::Segment* MachOLayout<A>::getSegment(const char* name) const
-{
- for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
- const Segment& seg = *it;
- if ( strcmp(seg.name(), name) == 0 )
- return &seg;
- }
- return NULL;
-}
-
-
-
-#endif // __MACHO_LAYOUT__
-
-
-
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __MACHO_REBASER__
-#define __MACHO_REBASER__
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach-o/reloc.h>
-#include <mach-o/x86_64/reloc.h>
-#include <mach-o/arm/reloc.h>
-#include <vector>
-#include <set>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-#include "MachOLayout.hpp"
-#include "MachOTrie.hpp"
-
-
-
-class AbstractRebaser
-{
-public:
- virtual cpu_type_t getArchitecture() const = 0;
- virtual uint64_t getBaseAddress() const = 0;
- virtual uint64_t getVMSize() const = 0;
- virtual bool rebase(std::vector<void*>&) = 0;
-};
-
-
-template <typename A>
-class Rebaser : public AbstractRebaser
-{
-public:
- Rebaser(const MachOLayoutAbstraction&);
- virtual ~Rebaser() {}
-
- virtual cpu_type_t getArchitecture() const;
- virtual uint64_t getBaseAddress() const;
- virtual uint64_t getVMSize() const;
- virtual bool rebase(std::vector<void*>&);
-
-protected:
- typedef typename A::P P;
- typedef typename A::P::E E;
- typedef typename A::P::uint_t pint_t;
-
- pint_t* mappedAddressForNewAddress(pint_t vmaddress);
- pint_t getSlideForNewAddress(pint_t newAddress);
-
-private:
- void adjustLoadCommands();
- void adjustSymbolTable();
- void adjustDATA();
- void adjustCode();
- void applyRebaseInfo(std::vector<void*>& pointersInData);
- void adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData);
- 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*>& pointersInData);
- bool adjustExportInfo();
- void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData);
- pint_t getSlideForVMAddress(pint_t vmaddress);
- pint_t maskedVMAddress(pint_t vmaddress);
- pint_t* mappedAddressForVMAddress(pint_t vmaddress);
- const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta);
- void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta);
- void doLocalRelocation(const macho_relocation_info<P>* reloc);
- bool unequalSlides() const;
-
-protected:
- const macho_header<P>* fHeader;
- uint8_t* fLinkEditBase; // add file offset to this to get linkedit content
- const MachOLayoutAbstraction& fLayout;
-private:
- const macho_symtab_command<P>* fSymbolTable;
- const macho_dysymtab_command<P>* fDynamicSymbolTable;
- const macho_dyld_info_command<P>* fDyldInfo;
- const macho_linkedit_data_command<P>* fSplitSegInfo;
- bool fSplittingSegments;
- bool fHasSplitSegInfoV2;
- std::vector<uint64_t> fSectionOffsetsInSegment;
-};
-
-
-template <typename A>
-Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
- : fLayout(layout), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL),
- fDyldInfo(NULL), fSplitSegInfo(NULL), fSplittingSegments(false), fHasSplitSegInfoV2(false)
-{
- fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
- switch ( fHeader->filetype() ) {
- case MH_DYLIB:
- case MH_BUNDLE:
- break;
- default:
- throw "file is not a dylib or bundle";
- }
-
- 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();
- break;
- }
- }
- if ( fLinkEditBase == NULL )
- throw "no __LINKEDIT segment";
-
- // get symbol table info
- 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:
- fSymbolTable = (macho_symtab_command<P>*)cmd;
- 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_SEGMENT_SPLIT_INFO:
- fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd;
- break;
- case macho_segment_command<P>::CMD: {
- // update segment/section file offsets
- macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- 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) {
- fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr());
- }
- }
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-
- if ( fDyldInfo == NULL )
- throw "no LC_DYLD_INFO load command";
-
- fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides();
-
- if ( fSplitSegInfo != NULL ) {
- const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
- fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT );
- }
-}
-
-template <> cpu_type_t Rebaser<x86>::getArchitecture() const { return CPU_TYPE_I386; }
-template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
-template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
-template <> cpu_type_t Rebaser<arm64>::getArchitecture() const { return CPU_TYPE_ARM64; }
-
-template <typename A>
-bool Rebaser<A>::unequalSlides() const
-{
- const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
- uint64_t slide = segments[0].newAddress() - segments[0].address();
- for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
- const MachOLayoutAbstraction::Segment& seg = *it;
- if ( (seg.newAddress() - seg.address()) != slide )
- return true;
- }
- return false;
-}
-
-template <typename A>
-uint64_t Rebaser<A>::getBaseAddress() const
-{
- return fLayout.getSegments()[0].address();
-}
-
-template <typename A>
-uint64_t Rebaser<A>::getVMSize() const
-{
- uint64_t highestVMAddress = 0;
- 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 ( seg.address() > highestVMAddress )
- highestVMAddress = seg.address();
- }
- return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096));
-}
-
-
-
-template <typename A>
-bool Rebaser<A>::rebase(std::vector<void*>& pointersInData)
-{
- if ( fHasSplitSegInfoV2 ) {
- this->adjustReferencesUsingInfoV2(pointersInData);
- }
- else {
- //fprintf(stderr, "warning: dylib with old split-seg info: %s\n", fLayout.getFilePath());
- // update writable segments that have internal pointers
- this->applyRebaseInfo(pointersInData);
-
- // if splitting segments, update code-to-data references
- this->adjustCode();
- }
-
- // update load commands
- this->adjustLoadCommands();
-
- // update symbol table
- this->adjustSymbolTable();
-
- // update export info
- return this->adjustExportInfo();
-}
-
-template <typename A>
-void Rebaser<A>::adjustLoadCommands()
-{
- 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_ID_DYLIB:
- if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
- // clear timestamp so that any prebound clients are invalidated
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- dylib->set_timestamp(1);
- }
- break;
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB:
- if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
- // clear expected timestamps so that this image will load with invalid prebinding
- macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
- dylib->set_timestamp(2);
- }
- break;
- case macho_routines_command<P>::CMD:
- // update -init command
- {
- struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
- routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address()));
- }
- break;
- case macho_segment_command<P>::CMD:
- // update segment commands
- {
- macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- pint_t slide = this->getSlideForVMAddress(seg->vmaddr());
- seg->set_vmaddr(seg->vmaddr() + slide);
- 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) {
- sect->set_addr(sect->addr() + slide);
- }
- }
- break;
- }
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
- }
-}
-
-template <>
-uint64_t Rebaser<arm64>::maskedVMAddress(pint_t vmaddress)
-{
- return (vmaddress & 0x0FFFFFFFFFFFFFFF);
-}
-
-template <typename A>
-typename A::P::uint_t Rebaser<A>::maskedVMAddress(pint_t vmaddress)
-{
- return vmaddress;
-}
-
-
-template <typename A>
-typename A::P::uint_t Rebaser<A>::getSlideForVMAddress(pint_t vmaddress)
-{
- pint_t vmaddr = this->maskedVMAddress(vmaddress);
- 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 ( (seg.address() <= vmaddr) && (seg.size() != 0) && ((vmaddr < (seg.address()+seg.size())) || (seg.address() == vmaddr)) ) {
- return seg.newAddress() - seg.address();
- }
- }
- throwf("vm address 0x%08llX not found", (uint64_t)vmaddr);
-}
-
-
-template <typename A>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(pint_t vmaddress)
-{
- pint_t vmaddr = this->maskedVMAddress(vmaddress);
- 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 ( (seg.address() <= vmaddr) && (vmaddr < (seg.address()+seg.size())) ) {
- return (pint_t*)((vmaddr - seg.address()) + (uint8_t*)seg.mappedAddress());
- }
- }
- throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddr);
-}
-
-template <typename A>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForNewAddress(pint_t vmaddress)
-{
- 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 ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) {
- return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress());
- }
- }
- throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress);
-}
-
-template <typename A>
-typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress)
-{
- 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 ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) {
- return seg.newAddress() - seg.address();
- }
- }
- throwf("new address 0x%08llX not found", (uint64_t)newAddress);
-}
-
-template <typename A>
-void Rebaser<A>::adjustSymbolTable()
-{
- macho_nlist<P>* symbolTable = (macho_nlist<P>*)(&fLinkEditBase[fSymbolTable->symoff()]);
-
- // walk all exports and slide their n_value
- macho_nlist<P>* lastExport = &symbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
- for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->iextdefsym()]; entry < lastExport; ++entry) {
- if ( (entry->n_type() & N_TYPE) == N_SECT )
- entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
- }
-
- // walk all local symbols and slide their n_value (don't adjust any stabs)
- macho_nlist<P>* lastLocal = &symbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
- for (macho_nlist<P>* entry = &symbolTable[fDynamicSymbolTable->ilocalsym()]; entry < lastLocal; ++entry) {
- if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
- entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value()));
- }
-}
-
-template <typename A>
-bool Rebaser<A>::adjustExportInfo()
-{
- // if no export info, nothing to adjust
- if ( fDyldInfo->export_size() == 0 )
- return true;
-
- // 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 = fLayout.getDyldInfoExports();
- const uint8_t* end = &start[fDyldInfo->export_size()];
- std::vector<mach_o::trie::Entry> originalExports;
- try {
- parseTrie(start, end, originalExports);
- }
- catch (const char* msg) {
- throwf("%s in %s", msg, fLayout.getFilePath());
- }
-
- std::vector<mach_o::trie::Entry> newExports;
- newExports.reserve(originalExports.size());
- pint_t baseAddress = this->getBaseAddress();
- pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress);
- for (std::vector<mach_o::trie::Entry>::iterator it=originalExports.begin(); it != originalExports.end(); ++it) {
- // remove symbols used by the static linker only
- if ( (strncmp(it->name, "$ld$", 4) == 0)
- || (strncmp(it->name, ".objc_class_name",16) == 0)
- || (strncmp(it->name, ".objc_category_name",19) == 0) ) {
- //fprintf(stderr, "ignoring symbol %s\n", it->name);
- continue;
- }
- // adjust symbols in slid segments
- //uint32_t oldOffset = it->address;
- it->address += (this->getSlideForVMAddress(it->address + baseAddress) - baseAddressSlide);
- //fprintf(stderr, "orig=0x%08X, new=0x%08llX, sym=%s\n", oldOffset, it->address, it->name);
- newExports.push_back(*it);
- }
-
- // rebuild export trie
- std::vector<uint8_t> newExportTrieBytes;
- newExportTrieBytes.reserve(fDyldInfo->export_size());
- mach_o::trie::makeTrie(newExports, newExportTrieBytes);
- // align
- while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 )
- newExportTrieBytes.push_back(0);
-
- uint32_t newExportsSize = newExportTrieBytes.size();
- if ( newExportsSize <= fDyldInfo->export_size() ) {
- // override existing trie in place
- uint8_t *realStart = &fLinkEditBase[fDyldInfo->export_off()];
- bzero(realStart, fDyldInfo->export_size());
- memcpy(realStart, &newExportTrieBytes[0], newExportsSize);
- fLayout.setDyldInfoExports(realStart);
- return true;
- }
- else {
- // allocate new buffer and set export_off in layout object to use new buffer instead
- uint8_t* sideTrie = new uint8_t[newExportsSize];
- memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
- fLayout.setDyldInfoExports(sideTrie);
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
- return false;
- }
-}
-
-
-
-template <typename A>
-void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta)
-{
- //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n",
- // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath());
- uint32_t* p;
- uint32_t instruction;
- uint32_t value;
- uint64_t value64;
- switch (kind) {
- case 1: // 32-bit pointer
- p = (uint32_t*)mappedAddressForVMAddress(address);
- value = A::P::E::get32(*p);
- value += codeToDataDelta;
- A::P::E::set32(*p, value);
- break;
- case 2: // 64-bit pointer
- p = (uint32_t*)mappedAddressForVMAddress(address);
- value64 = A::P::E::get64(*(uint64_t*)p);
- value64 += codeToDataDelta;
- A::P::E::set64(*(uint64_t*)p, value64);
- break;
- case 4: // only used for i386, a reference to something in the IMPORT segment
- p = (uint32_t*)mappedAddressForVMAddress(address);
- value = A::P::E::get32(*p);
- value += codeToImportDelta;
- A::P::E::set32(*p, value);
- break;
- case 5: // used by thumb2 movw
- p = (uint32_t*)mappedAddressForVMAddress(address);
- instruction = A::P::E::get32(*p);
- // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting
- value = (instruction & 0x0000000F) + (codeToDataDelta >> 12);
- instruction = (instruction & 0xFFFFFFF0) | (value & 0x0000000F);
- A::P::E::set32(*p, instruction);
- break;
- case 6: // used by ARM movw
- p = (uint32_t*)mappedAddressForVMAddress(address);
- instruction = A::P::E::get32(*p);
- // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting
- value = ((instruction & 0x000F0000) >> 16) + (codeToDataDelta >> 12);
- instruction = (instruction & 0xFFF0FFFF) | ((value <<16) & 0x000F0000);
- A::P::E::set32(*p, 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)
- {
- p = (uint32_t*)mappedAddressForVMAddress(address);
- instruction = A::P::E::get32(*p);
- // 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 + 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);
- A::P::E::set32(*p, 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)
- {
- p = (uint32_t*)mappedAddressForVMAddress(address);
- instruction = A::P::E::get32(*p);
- // 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 + 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_;
- A::P::E::set32(*p, newInstruction);
- }
- break;
- case 3: // used for arm64 ADRP
- p = (uint32_t*)mappedAddressForVMAddress(address);
- instruction = A::P::E::get32(*p);
- 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);
- A::P::E::set32(*p, instruction);
- }
- break;
- default:
- throwf("invalid kind=%d in split seg info", kind);
- }
-}
-
-template <typename A>
-const uint8_t* Rebaser<A>::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta)
-{
- uint64_t address = 0;
- uint64_t delta = 0;
- uint32_t shift = 0;
- bool more = true;
- do {
- uint8_t byte = *p++;
- delta |= ((byte & 0x7F) << shift);
- shift += 7;
- if ( byte < 0x80 ) {
- if ( delta != 0 ) {
- address += delta;
- doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta);
- delta = 0;
- shift = 0;
- }
- else {
- more = false;
- }
- }
- } while (more);
- return p;
-}
-
-template <typename A>
-void Rebaser<A>::adjustCode()
-{
- if ( fSplittingSegments ) {
- // get uleb128 compressed runs of code addresses to update
- const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
- const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];;
- // calculate how much we need to slide writable segments
- const uint64_t orgBaseAddress = this->getBaseAddress();
- int64_t codeToDataDelta = 0;
- int64_t codeToImportDelta = 0;
- const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
- const MachOLayoutAbstraction::Segment& codeSeg = segments[0];
- for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
- const MachOLayoutAbstraction::Segment& dataSeg = *it;
- if ( dataSeg.writable() ) {
- if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) )
- throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath());
- codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
- }
- }
- // decompress and call doCodeUpdate() on each address
- for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
- uint8_t kind = *p++;
- p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta);
- }
- }
-}
-
-template <typename A>
-void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData)
-{
- const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
- if ( segIndex > segments.size() )
- throw "bad segment index in rebase info";
- const MachOLayoutAbstraction::Segment& seg = segments[segIndex];
- uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segOffset;
- pint_t* mappedAddrP = (pint_t*)mappedAddr;
- uint32_t* mappedAddr32 = (uint32_t*)mappedAddr;
- pint_t valueP;
- pint_t valuePnew;
- uint32_t value32;
- int32_t svalue32;
- int32_t svalue32new;
- switch ( type ) {
- case REBASE_TYPE_POINTER:
- valueP= P::getP(*mappedAddrP);
- try {
- P::setP(*mappedAddrP, valueP + this->getSlideForVMAddress(valueP));
- }
- catch (const char* msg) {
- throwf("at offset=0x%08llX in seg=%s, pointer cannot be rebased because it does not point to __TEXT or __DATA. %s\n",
- segOffset, seg.name(), msg);
- }
- break;
-
- case REBASE_TYPE_TEXT_ABSOLUTE32:
- value32 = E::get32(*mappedAddr32);
- E::set32(*mappedAddr32, value32 + this->getSlideForVMAddress(value32));
- break;
-
- case REBASE_TYPE_TEXT_PCREL32:
- svalue32 = E::get32(*mappedAddr32);
- valueP = seg.address() + segOffset + 4 + svalue32;
- valuePnew = valueP + this->getSlideForVMAddress(valueP);
- svalue32new = seg.address() + segOffset + 4 - valuePnew;
- E::set32(*mappedAddr32, svalue32new);
- break;
-
- default:
- throw "bad rebase type";
- }
- pointersInData.push_back(mappedAddr);
-}
-
-
-template <typename A>
-void Rebaser<A>::applyRebaseInfo(std::vector<void*>& pointersInData)
-{
- const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()];
- const uint8_t* end = &p[fDyldInfo->rebase_size()];
-
- uint8_t type = 0;
- int segIndex;
- uint64_t segOffset = 0;
- uint32_t count;
- uint32_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) {
- doRebase(segIndex, segOffset, type, pointersInData);
- 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) {
- doRebase(segIndex, segOffset, type, pointersInData);
- segOffset += sizeof(pint_t);
- }
- break;
- case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- doRebase(segIndex, segOffset, type, pointersInData);
- 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) {
- doRebase(segIndex, segOffset, type, pointersInData);
- segOffset += skip + sizeof(pint_t);
- }
- break;
- default:
- throwf("bad rebase opcode %d", *p);
- }
- }
-}
-
-template <>
-void Rebaser<arm64>::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*>& pointersInData)
-{
- uint64_t value64;
- uint64_t* mappedAddr64;
- uint32_t value32;
- uint32_t* mappedAddr32;
- uint32_t instruction;
- int64_t offsetAdjust;
- switch ( kind ) {
- case DYLD_CACHE_ADJ_V2_DELTA_32:
- mappedAddr32 = (uint32_t*)mappedAddr;
- value32 = arm64::P::E::get32(*mappedAddr32);
- E::set32(*mappedAddr32, value32 + adjust);
- break;
- case DYLD_CACHE_ADJ_V2_POINTER_64:
- mappedAddr64 = (uint64_t*)mappedAddr;
- if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) )
- throwf("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX\n", fromNewAddress);
- E::set64(*mappedAddr64, toNewAddress);
- pointersInData.push_back(mappedAddr);
- break;
- case DYLD_CACHE_ADJ_V2_DELTA_64:
- mappedAddr64 = (uint64_t*)mappedAddr;
- value64 = arm64::P::E::get64(*mappedAddr64);
- E::set64(*mappedAddr64, value64 + adjust);
- break;
- case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
- mappedAddr32 = (uint32_t*)mappedAddr;
- value64 = toNewAddress - imageStartAddress;
- E::set32(*mappedAddr32, (uint32_t)value64);
- break;
- case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
- mappedAddr32 = (uint32_t*)mappedAddr;
- instruction = arm64::P::E::get32(*mappedAddr32);
- if ( (instruction & 0x9F000000) == 0x90000000 ) {
- //value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9);
- uint32_t newPage21 = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)) >> 12;
- instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
- arm64::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 = arm64::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;
- switch ( instruction & 0xC0000000 ) {
- case 0x00000000:
- if ( (instruction & 0x04800000) == 0x04800000 ) {
- if ( offsetAdjust & 0xF )
- throwf("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
- if ( encodedAddend*16 >= 4096 )
- throwf("off12 scale=16 instruction points outside its page at mapped address=%p", mappedAddr);
- newAddend = (encodedAddend + offsetAdjust/16) % 256;
- }
- else {
- // scale=1
- newAddend = encodedAddend + offsetAdjust;
- }
- break;
- case 0x40000000:
- if ( offsetAdjust & 1 )
- throwf("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
- if ( encodedAddend*2 >= 4096 )
- throwf("off12 scale=2 instruction points outside its page at mapped address=%p", mappedAddr);
- newAddend = (encodedAddend + offsetAdjust/2) % 2048;
- break;
- case 0x80000000:
- if ( offsetAdjust & 3 )
- throwf("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
- if ( encodedAddend*4 >= 4096 )
- throwf("off12 scale=4 instruction points outside its page at mapped address=%p", mappedAddr);
- newAddend = (encodedAddend + offsetAdjust/4) % 1024;
- break;
- case 0xC0000000:
- if ( offsetAdjust & 7 )
- throwf("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
- if ( encodedAddend*8 >= 4096 )
- throwf("off12 scale=8 instruction points outside its page at mapped address=%p", mappedAddr);
- newAddend = (encodedAddend + offsetAdjust/8) % 512;
- break;
- }
- uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
- arm64::P::E::set32(*mappedAddr32, newInstruction);
- }
- }
- else if ( (instruction & 0xFFC00000) == 0x91000000 ) {
- // ADD imm12
- if ( instruction & 0x00C00000 )
- throwf("ADD off12 uses shift at mapped address=%p", mappedAddr);
- uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
- uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF;
- uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
- arm64::P::E::set32(*mappedAddr32, newInstruction);
- }
- else if ( instruction != 0xD503201F ) {
- // ignore imm12 instructions optimized into a NOP, but warn about others
- fprintf(stderr, "unknown off12 instruction 0x%08X at 0x%0llX in %s\n", instruction, fromNewAddress, fLayout.getFilePath());
- }
- break;
- case DYLD_CACHE_ADJ_V2_ARM64_BR26:
- // nothing to do with calls to stubs
- break;
- default:
- throwf("unknown split seg kind=%d", kind);
- }
-}
-
-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 <>
-void Rebaser<arm>::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*>& pointersInData)
-{
- uint32_t value32;
- uint32_t* mappedAddr32 = (uint32_t*)mappedAddr;
- static uint32_t* lastMappedAddr32 = NULL;
- static uint32_t lastKind = 0;
- static uint32_t lastToNewAddress = 0;
- switch ( kind ) {
- case DYLD_CACHE_ADJ_V2_DELTA_32:
- value32 = arm64::P::E::get32(*mappedAddr32);
- E::set32(*mappedAddr32, value32 + adjust);
- break;
- case DYLD_CACHE_ADJ_V2_POINTER_32:
- if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) )
- throwf("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX\n", fromNewAddress);
- E::set32(*mappedAddr32, toNewAddress);
- 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);
- break;
- case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT:
- // 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 = E::get32(*lastMappedAddr32);
- uint32_t instruction2 = 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 {
- throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not";
- }
- E::set32(*lastMappedAddr32, instruction1);
- E::set32(*mappedAddr32, instruction2);
- kind = 0;
- }
- else {
- throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses";
- }
- }
- break;
- case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT:
- // 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 = E::get32(*lastMappedAddr32);
- uint32_t instruction2 = 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 {
- throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not";
- }
- E::set32(*lastMappedAddr32, instruction1);
- E::set32(*mappedAddr32, instruction2);
- kind = 0;
- }
- else {
- throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses";
- }
- }
- break;
- case DYLD_CACHE_ADJ_V2_ARM_BR24:
- case DYLD_CACHE_ADJ_V2_THUMB_BR22:
- // nothing to do with calls to stubs
- break;
- default:
- throwf("v2 split seg info kind (%d) not supported yet", kind);
- }
- lastKind = kind;
- lastToNewAddress = toNewAddress;
- lastMappedAddr32 = mappedAddr32;
-}
-
-
-template <typename A>
-void Rebaser<A>::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*>& pointersInData)
-{
- throw "v2 split seg info not supported yet";
-}
-
-
-template <typename A>
-void Rebaser<A>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData)
-{
- static const bool log = false;
-
- const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
- const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];;
- if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT )
- throw "malformed split seg info";
-
- // 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
- const MachOLayoutAbstraction::Segment& textSeg = fLayout.getSegments().front();
- sectionMappedAddress.push_back((uint8_t*)textSeg.mappedAddress());
- sectionSlides.push_back(textSeg.newAddress() - textSeg.address());
- sectionNewAddress.push_back(textSeg.newAddress());
- // section 1 and later refer to real sections
- unsigned sectionIndex = 0;
- for (const MachOLayoutAbstraction::Segment& seg : fLayout.getSegments()) {
- uint64_t segSlide = seg.newAddress() - seg.address();
- for (uint32_t i=0; i < seg.sectionCount(); ++i) {
- if (log) fprintf(stderr, "seg=%s, sectIndex=%d, mapped at=%p, offsetInSeg=0x%08llX\n", seg.name(), sectionIndex, seg.mappedAddress(), fSectionOffsetsInSegment[sectionIndex]);
- sectionMappedAddress.push_back((uint8_t*)seg.mappedAddress() + fSectionOffsetsInSegment[sectionIndex]);
- sectionSlides.push_back(segSlide);
- sectionNewAddress.push_back(seg.newAddress() + fSectionOffsetsInSegment[sectionIndex]);
- ++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) {
- 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);
- 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;
- adjustReference(kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, sectionNewAddress.front(), sectionNewAddress.back(), pointersInData);
- }
- }
- }
- }
-
-
-}
-
-#endif // __MACHO_REBASER__
-
-
-
-
optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
// update fat header with new file size
- dylib_data.resize(offsetInFatFile+newSize);
+ dylib_data.resize((size_t)(offsetInFatFile+newSize));
base_ptr = &dylib_data.front();
FA->size = OSSwapHostToBigInt32(newSize);
#undef FH
return -1;
}
- void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ void* mapped_cache = mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
if (mapped_cache == MAP_FAILED) {
fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno);
return -1;
dylib_create_func = dylib_maker<arm>;
else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 )
dylib_create_func = dylib_maker<arm64>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64e") == 0 )
+ dylib_create_func = dylib_maker<arm64>;
else {
fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
- munmap(mapped_cache, statbuf.st_size);
+ munmap(mapped_cache, (size_t)statbuf.st_size);
return -1;
}
if(result != 0) {
fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
- munmap(mapped_cache, statbuf.st_size);
+ munmap(mapped_cache, (size_t)statbuf.st_size);
return result;
}
return;
}
- std::vector<uint8_t> *vec = new std::vector<uint8_t>(statbuf.st_size);
+ std::vector<uint8_t> *vec = new std::vector<uint8_t>((size_t)statbuf.st_size);
if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) {
fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno);
close(fd);
dispatch_release(group);
dispatch_release(writer_queue);
- munmap(mapped_cache, statbuf.st_size);
+ munmap(mapped_cache, (size_t)statbuf.st_size);
return result;
}
// call the callback block on each segment in this image
template <typename A>
- int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader,
+ int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, uint64_t cache_unslid_base_address,
void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo))
{
typedef typename A::P P;
if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) )
sizem = (cacheEnd-cache)-fileOffset;
}
- segInfo.version = 1;
+ segInfo.version = 2;
segInfo.name = segCmd->segname();
segInfo.fileOffset = fileOffset;
segInfo.fileSize = sizem;
if ( segCmd->filesize() > segCmd->vmsize() )
return -1;
segInfo.address = segCmd->vmaddr();
+ segInfo.addressOffset = segInfo.address - cache_unslid_base_address;
callback(&dylibInfo, &segInfo);
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
const dyldCacheImageInfo<E>* dylibs = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
+ uint64_t unslid_base_address = mappings[0].address();
uint64_t greatestMappingOffset = 0;
for (uint32_t i=0; i < header->mappingCount(); ++i) {
if ( (size != 0) && (mappings[i].file_offset() > size) )
return -1;
if ( firstSeg == NULL )
firstSeg = machHeader;
- int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, callback);
+ int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback);
if ( result != 0 )
return result;
}
return dyld::walkImages<arm>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 )
return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
+ else if ( strcmp((char*)cache, "dyld_v1 arm64e") == 0 )
+ return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
else
return -1;
}
struct dyld_shared_cache_segment_info {
uint64_t version; // initial version 1
+ // following fields exist in version 1
const char* name; // of segment
uint64_t fileOffset; // of segment in cache file
uint64_t fileSize; // of segment
uint64_t address; // of segment when cache mapped with ASLR (sliding) off
- // above fields all exist in version 1
+ // following fields exist in version 2
+ uint64_t addressOffset; // of segment from base of mapped cache
};
typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info;
uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries
};
+
struct dyld_cache_mapping_info {
uint64_t address;
uint64_t size;
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
return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f";
#elif __ARM_ARCH_7S__
return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s";
-#elif __arm64__
+#elif __arm64e__
+ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e";
+#elif __arm64__
return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
#else
#error unsupported architecture
info.textSize = segInfo->fileSize;
info.path = dylibInfo->path;
results.textSegments.push_back(info);
- size_t size = segInfo->fileSize;
}
fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
exit(1);
}
- options.mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ options.mappedCache = ::mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
if (options.mappedCache == MAP_FAILED) {
fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno);
exit(1);
else {
printf("n/a\n");
}
+ if ( header->mappingOffset() >= 0xE0 ) {
+ // HACK until this uses new header
+ uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
+ uint32_t simulator = *((uint32_t*)(((char*)header) + 0xDC));
+ switch (platform) {
+ case 1:
+ printf("platform: macOS\n");
+ break;
+ case 2:
+ if ( simulator & 0x400 )
+ printf("platform: iOS simulator\n");
+ else
+ printf("platform: iOS\n");
+ break;
+ case 3:
+ if ( simulator & 0x400 )
+ printf("platform: tvOS simulator\n");
+ else
+ printf("platform: tvOS\n");
+ break;
+ case 4:
+ if ( simulator & 0x400 )
+ printf("platform: watchOS simulator\n");
+ else
+ printf("platform: watchOS\n");
+ break;
+ case 5:
+ printf("platform: bridgeOS\n");
+ break;
+ default:
+ printf("platform: 0x%08X 0x%08X\n", platform, simulator);
+ }
+ }
printf("image count: %u\n", header->imagesCount());
if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
printf("branch pool count: %u\n", header->branchPoolsCount());
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;
+ //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);
return result;
}
else {
- segment_callback_t callback;
+ segment_callback_t callback = nullptr;
if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) {
switch ( options.mode ) {
case modeList:
break;
}
}
- else if ( strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0 ) {
+ else if ( (strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0)
+ || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0) ) {
switch ( options.mode ) {
case modeList:
callback = print_list<arm64>;
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>com.apple.rootless.storage.dyld</key>
- <true/>
- </dict>
-</plist>
bool depLibRequired = false;
bool depLibCheckSumsMatch = false;
DependentLibraryInfo& requiredLibInfo = libraryInfos[i];
-#if DYLD_SHARED_CACHE_SUPPORT
if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) {
// <rdar://problem/5910137> dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized
// in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded
setLibImage(i, NULL, false, false);
continue;
}
-#endif
try {
unsigned cacheIndex;
dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex);
bit += 7;
}
} while (*p++ & 0x80);
- return result;
+ return (uintptr_t)result;
}
// sign extend negative numbers
if ( (byte & 0x40) != 0 )
result |= (-1LL) << bit;
- return result;
+ return (intptr_t)result;
}
#define SPLIT_SEG_DYLIB_SUPPORT 0
#define PREBOUND_IMAGE_SUPPORT __arm__
#define TEXT_RELOC_SUPPORT __i386__
- #define DYLD_SHARED_CACHE_SUPPORT (__arm__ || __arm64__)
#define SUPPORT_OLD_CRT_INITIALIZATION 0
#define SUPPORT_LC_DYLD_ENVIRONMENT 1
#define SUPPORT_VERSIONED_PATHS 1
#define SPLIT_SEG_DYLIB_SUPPORT __i386__
#define PREBOUND_IMAGE_SUPPORT __i386__
#define TEXT_RELOC_SUPPORT __i386__
- #define DYLD_SHARED_CACHE_SUPPORT 1
#define SUPPORT_OLD_CRT_INITIALIZATION __i386__
#define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__)
#define SUPPORT_VERSIONED_PATHS 1
dyld_image_states getState() { return (dyld_image_states)fState; }
ino_t getInode() const { return fInode; }
+ dev_t getDevice() const { return fDevice; }
// used to sort images bottom-up
int compare(const ImageLoader* right) const;
#include "ImageLoaderMachOClassic.h"
#endif
#include "mach-o/dyld_images.h"
+#include "Tracing.h"
#include "dyld.h"
// <rdar://problem/8718137> use stack guard random value to add padding between dylibs
#define LC_VERSION_MIN_WATCHOS 0x30
#endif
+#ifndef LC_BUILD_VERSION
+ #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+ /*
+ * The build_version_command contains the min OS version on which this
+ * binary was built to run for its platform. The list of known platforms and
+ * tool values following it.
+ */
+ struct build_version_command {
+ uint32_t cmd; /* LC_BUILD_VERSION */
+ uint32_t cmdsize; /* sizeof(struct build_version_command) plus */
+ /* ntools * sizeof(struct build_tool_version) */
+ uint32_t platform; /* platform */
+ uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t ntools; /* number of tool entries following this */
+ };
+
+ struct build_tool_version {
+ uint32_t tool; /* enum for the tool */
+ uint32_t version; /* version number of the tool */
+ };
+
+ /* Known values for the platform field above. */
+ #define PLATFORM_MACOS 1
+ #define PLATFORM_IOS 2
+ #define PLATFORM_TVOS 3
+ #define PLATFORM_WATCHOS 4
+ #define PLATFORM_BRIDGEOS 5
+
+ /* Known values for the tool field above. */
+ #define TOOL_CLANG 1
+ #define TOOL_SWIFT 2
+ #define TOOL_LD 3
+#endif
+
+
#if TARGET_IPHONE_SIMULATOR
#define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
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->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 ) {
throw "load commands not in a segment";
if ( linkeditSegCmd == NULL )
throw "malformed mach-o image: missing __LINKEDIT segment";
- if ( startOfFileSegCmd == NULL )
+ if ( !inCache && (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.strictMachORequired ) {
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;
+ const struct build_version_command* buildVersCmd;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch ( cmd->cmd ) {
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_WATCHOS:
versCmd = (version_min_command*)cmd;
return versCmd->sdk;
+ case LC_BUILD_VERSION:
+ buildVersCmd = (build_version_command*)cmd;
+ return buildVersCmd->sdk;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
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;
+ const struct build_version_command* buildVersCmd;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch ( cmd->cmd ) {
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_WATCHOS:
versCmd = (version_min_command*)cmd;
return versCmd->version;
+ case LC_BUILD_VERSION:
+ buildVersCmd = (build_version_command*)cmd;
+ return buildVersCmd->minos;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
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) )
+ if ( strcmp(seg->segname, "__TEXT") == 0 )
return (char*)mh - (char*)(seg->vmaddr);
}
cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize);
// These are defined in dyldStartup.s
extern "C" void stub_binding_helper();
-
+extern "C" int _dyld_func_lookup(const char* name, void** address);
void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
{
}
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);
+ dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+ });
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
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);
+ dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ 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
}
// map in all segments
for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
- vm_offset_t fileOffset = segFileOffset(i) + offsetInFat;
+ vm_offset_t fileOffset = (vm_offset_t)(segFileOffset(i) + offsetInFat);
vm_size_t size = segFileSize(i);
uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
int protection = 0;
unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff);
linkEditBaseFound = true;
}
- else if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+ else if ( strcmp(seg->segname, "__TEXT") == 0 ) {
slide = (uintptr_t)mh - seg->vmaddr;
+ }
break;
case LC_SYMTAB:
symtab = (symtab_command*)cmd;
while ( ! foundRoom ) {
foundRoom = true;
for(unsigned int i=0; i < regionCount; ++i) {
- vm_address_t addr = nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address;
- vm_size_t size = regions[i].sfm_size ;
+ vm_address_t addr = (vm_address_t)(nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address);
+ vm_size_t size = (vm_size_t)regions[i].sfm_size ;
r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/);
if ( 0 != r ) {
// no room here, deallocate what has succeeded so far
for(unsigned int j=0; j < i; ++j) {
- addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address;
- size = regions[j].sfm_size ;
+ addr = (vm_address_t)(nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address);
+ size = (vm_size_t)(regions[j].sfm_size);
(void)vm_deallocate(mach_task_self(), addr, size);
}
nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again
}
// map in each region
- uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address;
+ uintptr_t slide = (uintptr_t)(nextAltLoadAddress - regions[0].sfm_address);
this->setSlide(slide);
for(unsigned int i=0; i < regionCount; ++i) {
if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) {
}
else {
void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide);
- size_t size = regions[i].sfm_size;
+ size_t size = (size_t)regions[i].sfm_size;
int protection = 0;
if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE )
protection |= PROT_EXEC;
{
// loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values
const uintptr_t relocBase = this->getRelocBase();
- register const uintptr_t slide = this->fSlide;
+ const uintptr_t slide = this->fSlide;
const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]);
const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel];
for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
+#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach/thread_status.h>
#include <mach-o/loader.h>
struct macho_routines_command : public routines_command {};
#endif
+#if __arm__ || __arm64__
+bool ImageLoaderMachOCompressed::sVmAccountingDisabled = false;
+bool ImageLoaderMachOCompressed::sVmAccountingSuspended = false;
+#endif
+
+
// create image for main executable
ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path,
// make sure path is stable before recording in dyld_all_image_infos
image->setMapped(context);
+ // dylibs with thread local variables cannot be unloaded because there is no way to clean up all threads
+ if ( image->machHeader()->flags & MH_HAS_TLV_DESCRIPTORS )
+ image->setNeverUnload();
+
// pre-fetch content of __DATA and __LINKEDIT segment for faster launches
// don't do this on prebound images or if prefetching is disabled
if ( !context.preFetchDisabled && !image->isPrebindable()) {
eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
}
+#if __arm__ || __arm64__
+int ImageLoaderMachOCompressed::vmAccountingSetSuspended(bool suspend, const LinkContext& context)
+{
+ if ( context.verboseBind )
+ dyld::log("vm.footprint_suspend=%d\n", suspend);
+ int newValue = suspend ? 1 : 0;
+ int oldValue = 0;
+ size_t newlen = sizeof(newValue);
+ size_t oldlen = sizeof(oldValue);
+ return sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+}
+#endif
+
void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler)
{
+#if __arm__ || __arm64__
+ // <rdar://problem/29099600> dyld should tell the kernel when it is doing root fix-ups
+ if ( !sVmAccountingDisabled ) {
+ if ( fInSharedCache ) {
+ if ( !sVmAccountingSuspended ) {
+ int ret = vmAccountingSetSuspended(true, context);
+ if ( context.verboseBind && (ret != 0) )
+ dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno);
+ if ( ret == 0 )
+ sVmAccountingSuspended = true;
+ else
+ sVmAccountingDisabled = true;
+ }
+ }
+ else if ( sVmAccountingSuspended ) {
+ int ret = vmAccountingSetSuspended(false, context);
+ if ( ret == 0 )
+ sVmAccountingSuspended = false;
+ else if ( errno == ENOENT )
+ sVmAccountingDisabled = true;
+ }
+ }
+#endif
+
try {
uint8_t type = 0;
int segmentIndex = -1;
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);
-
+
const struct dyld_info_command* fDyldInfo;
+
+#if __arm__ || __arm64__
+ static int vmAccountingSetSuspended(bool suspend, const LinkContext& context);
+ static bool sVmAccountingDisabled; // sysctl not availble
+ static bool sVmAccountingSuspended; // kernel is currently ignoring COWs
+#endif
};
#include "ImageLoaderMachO.h"
#include "mach-o/dyld_images.h"
#include "dyldLibSystemInterface.h"
+#include "Tracing.h"
#include "dyld.h"
// from dyld_gdb.cpp
extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
+extern "C" int _dyld_func_lookup(const char* name, void** address);
+
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#if SUPPORT_ACCELERATE_TABLES
-ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context)
{
- return new ImageLoaderMegaDylib(header, slide, context);
+ return new ImageLoaderMegaDylib(header, slide, mainMH, context);
}
struct DATAdyld {
-ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context)
: ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide),
_lockArray(NULL), _lockArrayInUseCount(0)
{
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();
+ dyldSection->vars.mh = mainMH;
context.setNewProgramVars(dyldSection->vars);
}
if ( strncmp(path, "@rpath/", 7) == 0 ) {
std::vector<const char*> rpathsFromMainExecutable;
context.mainExecutable->getRPaths(context, rpathsFromMainExecutable);
+ rpathsFromMainExecutable.push_back("/System/Library/Frameworks/");
const char* trailingPath = &path[7];
for (const char* anRPath : rpathsFromMainExecutable) {
if ( anRPath[0] != '/' )
}
}
}
-
- // handle symlinks embedded in load commands
- char resolvedPath[PATH_MAX];
- realpath(path, resolvedPath);
- int realpathErrno = errno;
- // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
- if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) {
- if ( strcmp(resolvedPath, path) != 0 )
- return findImageIndex(context, resolvedPath);
- }
-
+ else {
+ // handle symlinks embedded in load commands
+ char resolvedPath[PATH_MAX];
+ realpath(path, resolvedPath);
+ int realpathErrno = errno;
+ // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+ if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) {
+ if ( strcmp(resolvedPath, path) != 0 )
+ return findImageIndex(context, resolvedPath);
+ }
+ }
dyld::throwf("no cache image with name (%s)", path);
}
void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex,
- InitializerTimingList& timingInfo)
+ InitializerTimingList& timingInfo, UpwardIndexes& upInits)
{
// Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array
bool useLock = dyld::gProcessInfo->libSystemInitialized;
unsigned subDepIndex = _dependenciesArray[i];
// ignore upward links
if ( (subDepIndex & 0x8000) == 0 )
- recursiveInitialization(context, thisThread, subDepIndex, timingInfo);
+ recursiveInitialization(context, thisThread, subDepIndex, timingInfo, upInits);
+ else
+ upInits.images[upInits.count++] = (subDepIndex & 0x7FFF);
}
// notify objc about this image
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);
+ dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+ });
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
ranSomeInitializers = true;
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize,
- InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
+ InitializerTimingList& timingInfo, UninitedUpwards&)
{
unsigned imageIndex;
if ( hasDylib(pathToInitialize, &imageIndex) ) {
- this->recursiveInitialization(context, thisThread, imageIndex, timingInfo);
+ UpwardIndexes upsBuffer[256];
+ UpwardIndexes& ups = upsBuffer[0];
+ ups.count = 0;
+ this->recursiveInitialization(context, thisThread, imageIndex, timingInfo, ups);
+ for (int i=0; i < ups.count; ++i) {
+ UpwardIndexes upsBuffer2[256];
+ UpwardIndexes& ignoreUp = upsBuffer2[0];
+ ignoreUp.count = 0;
+ this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo, ignoreUp);
+ }
}
}
uint8_t targetCacheState = dyldStateToCacheState(state);
if ( targetCacheState == kStateUnused )
return 0;
-
unsigned usedCount = 0;
- for (int i=_imageCount-1; i > 0; --i) {
+ for (int i=_imageCount-1; i >= 0; --i) {
uint16_t index = _bottomUpArray[i];
uint8_t imageState = _stateFlags[index];
if ( imageState == kStateFlagWeakBound )
InitializerTimingList timingInfo[_initializerCount];
timingInfo[0].count = 0;
mach_port_t thisThread = mach_thread_self();
- this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0]);
+ UpwardIndexes upsBuffer[256]; // room for 511 dangling upward links
+ UpwardIndexes& ups = upsBuffer[0];
+ ups.count = 0;
+ this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0], ups);
+ // make sure any upward linked dylibs were initialized
+ for (int i=0; i < ups.count; ++i) {
+ UpwardIndexes upsBuffer2[256];
+ UpwardIndexes& ignoreUp = upsBuffer2[0];
+ ignoreUp.count = 0;
+ this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo[0], ignoreUp);
+ }
mach_port_deallocate(mach_task_self(), thisThread);
context.notifyBatch(dyld_image_state_initialized, false);
}
//
class ImageLoaderMegaDylib : public ImageLoader {
public:
- static ImageLoaderMegaDylib* makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+ static ImageLoaderMegaDylib* makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&);
virtual ~ImageLoaderMegaDylib();
virtual void setWeakSymbolsBound(unsigned index);
private:
- ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+ ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&);
+
+ struct UpwardIndexes
+ {
+ uint16_t count;
+ uint16_t images[1];
+ };
const macho_header* getIndexedMachHeader(unsigned index) const;
const uint8_t* getIndexedTrie(unsigned index, uint32_t& trieSize) const;
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);
+ InitializerTimingList& timingInfo, UpwardIndexes&);
void recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread);
void recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread);
#include <sys/un.h>
#include <sys/syslog.h>
#include <sys/uio.h>
+#include <mach/mach.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/ldsyms.h>
#include <sandbox.h>
#include <sandbox/private.h>
+extern "C" int __fork();
+
#include <array>
+#include <algorithm>
+#include <vector>
#ifndef CPU_SUBTYPE_ARM_V5TEJ
#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8)
#endif
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
+
#ifndef VM_PROT_SLIDE
#define VM_PROT_SLIDE 0x20
#endif
-#include <vector>
-#include <algorithm>
-
#include "mach-o/dyld_gdb.h"
#include "dyld.h"
#include "ImageLoader.h"
#include "ImageLoaderMachO.h"
#include "dyldLibSystemInterface.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
#include "dyldSyscallInterface.h"
#endif
+#include "LaunchCache.h"
+#include "libdyldEntryVector.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "DyldSharedCache.h"
+#include "SharedCacheRuntime.h"
+#include "StringUtils.h"
+#include "Tracing.h"
+#include "DyldCacheParser.h"
+
+extern "C" {
+ #include "closuredProtocol.h"
+}
+
// not libc header for send() syscall interface
extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
// ARM and x86_64 are the only architecture that use cpu-sub-types
-#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)
+#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __arm64__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)
#if __LP64__
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
bool DYLD_PRINT_ENV;
bool DYLD_DISABLE_DOFS;
bool DYLD_PRINT_CS_NOTIFICATIONS;
- // DYLD_SHARED_CACHE_DONT_VALIDATE ==> sSharedCacheIgnoreInodeAndTimeStamp
- // DYLD_SHARED_CACHE_DIR ==> sSharedCacheDir
+ // DYLD_SHARED_CACHE_DIR ==> sSharedCacheOverrideDir
// DYLD_ROOT_PATH ==> gLinkContext.rootPaths
// DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix
// DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts
static const char* sExecPath = NULL;
static const char* sExecShortName = NULL;
static const macho_header* sMainExecutableMachHeader = NULL;
+static uintptr_t sMainExecutableSlide = 0;
#if CPU_SUBTYPES_SUPPORTED
static cpu_type_t sHostCPU;
static cpu_subtype_t sHostCPUsubtype;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL };
static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL };
+static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
+static const char* sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL };
#else
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
-static const dyld_cache_header* sSharedCache = NULL;
-static long sSharedCacheSlide = 0;
-static bool sSharedCacheIgnoreInodeAndTimeStamp = false;
- bool gSharedCacheOverridden = false;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR;
- #define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024
-#else
- static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
-#endif
-#endif
+static dyld3::SharedCacheLoadInfo sSharedCacheLoadInfo;
+static const char* sSharedCacheOverrideDir;
+ bool gSharedCacheOverridden = false;
ImageLoader::LinkContext gLinkContext;
bool gLogAPIs = false;
#if SUPPORT_ACCELERATE_TABLES
static int sLogSocket = -1;
#endif
static bool sFrameworksFoundAsDylibs = false;
-#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
static bool sHaswell = false;
#endif
static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
#endif
-
#if SUPPORT_ACCELERATE_TABLES
static ImageLoaderMegaDylib* sAllCacheImagesProxy = NULL;
static bool sDisableAcceleratorTables = false;
#endif
+bool gUseDyld3 = false;
+static bool sSkipMain = false;
+static bool sEnableClosures = false;
+
//
// 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
va_end(list);
}
-
+#else
+ extern void vlog(const char* format, va_list list);
#endif // !TARGET_IPHONE_SIMULATOR
#endif
static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
-
+static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[])
{
+ if ( sZombieNotifiers[portSlot] )
+ return;
+
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
+ unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + 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.
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);
+ kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 5000, 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::gProcessInfo->notifyPorts[portSlot] = 0;
sNotifyReplyPorts[portSlot] = 0;
}
+ else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+ // client took too long, ignore him from now on
+ sZombieNotifiers[portSlot] = true;
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+ sNotifyReplyPorts[portSlot] = 0;
+ }
}
static void notifyMonitoringDyldMain()
{
for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
- if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) {
+ if ( (dyld::gProcessInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
if ( sNotifyReplyPorts[slot] == 0 ) {
if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
//dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
}
//dyld::log("found port to send to\n");
- mach_msg_header_t h;
- 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 = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
- h.msgh_local_port = sNotifyReplyPorts[slot];
- h.msgh_remote_port = dyld::gProcessInfo->notifyPorts[slot];
- h.msgh_reserved = 0;
- h.msgh_size = (mach_msg_size_t)sizeof(mach_msg_header_t);
- //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, dyld::gProcessInfo->notifyPorts[slot], h.msgh_size, sNotifyReplyPorts[slot], 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[slot], 100, MACH_PORT_NULL);
- //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h.msgh_id, h.msgh_size);
+ uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+ mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+ 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 = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
+ h->msgh_local_port = sNotifyReplyPorts[slot];
+ h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[slot];
+ h->msgh_reserved = 0;
+ h->msgh_size = (mach_msg_size_t)sizeof(messageBuffer);
+ //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, dyld::gProcessInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id);
+ kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 5000, 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[slot], sNotifyReplyPorts[slot]);
dyld::gProcessInfo->notifyPorts[slot] = 0;
sNotifyReplyPorts[slot] = 0;
}
+ else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+ // client took too long, ignore him from now on
+ sZombieNotifiers[slot] = true;
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+ sNotifyReplyPorts[slot] = 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) {
+void notifyKernel(const ImageLoader& image, bool loading) {
if ( !image.inSharedCache() ) {
+ uint32_t baseCode = loading ? DBG_DYLD_UUID_MAP_A : DBG_DYLD_UUID_UNMAP_A;
+ uuid_t uuid;
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++;
+ image.getUUID(uuid);
+ dyld3::kdebug_trace_dyld_image(baseCode, (const uuid_t *)&uuid, *(fsobj_id_t*)&inode, {{ image.getDevice(), 0 }}, image.machHeader());
}
- 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("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
sNotifyReplyPorts[slot] = 0;
+ sZombieNotifiers[slot] = false;
}
}
}
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);
- 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();
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);
+ notifyKernel(*image, true);
// special case for add_image hook
if ( state == dyld_image_state_bound )
notifyAddImageCallbacks(image);
}
- flushKernelNotifications(true, true, kernelInfos, kernelInfoCount);
}
#if SUPPORT_ACCELERATE_TABLES
if ( sAllCacheImagesProxy != NULL ) {
if ( state == dyld_image_state_bound ) {
for (ImageCallback callback : sAddImageCallbacks) {
for (unsigned i=0; i < cacheCount; ++i)
- (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide);
+ (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
}
}
imageCount += cacheCount;
}
#endif
+
+
static void printOptions(const char* argv[])
{
uint32_t i = 0;
dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n");
}
}
-#if DYLD_SHARED_CACHE_SUPPORT
else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && !sSafeMode ) {
- sSharedCacheDir = value;
+ sSharedCacheOverrideDir = value;
}
- else if ( (strcmp(key, "DYLD_SHARED_CACHE_DONT_VALIDATE") == 0) && !sSafeMode ) {
- sSharedCacheIgnoreInodeAndTimeStamp = true;
+ else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) {
+ if ( dyld3::loader::internalInstall() )
+ sEnableClosures = true;
}
-#endif
else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
if ( strcmp(value, "all") == 0 ) {
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno);
}
}
+ else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) {
+ if ( dyld3::loader::internalInstall() )
+ sSkipMain = true;
+ }
#endif
else {
dyld::warn("unknown environment variable: %s\n", key);
checkLoadCommandEnvironmentVariables();
#endif
+ // Are we testing dyld on an internal config?
+ if ( _simple_getenv(envp, "DYLD_SKIP_MAIN") != NULL ) {
+ if ( dyld3::loader::internalInstall() )
+ sSkipMain = true;
+ }
+
// delete all DYLD_* and LD_LIBRARY_PATH environment variables
int removedCount = 0;
const char** d = envp;
for(const char** s = envp; *s != NULL; s++) {
+
if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) {
*d++ = *s;
}
#endif
}
-#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
static bool isGCProgram(const macho_header* mh, uintptr_t slide)
{
const uint32_t cmd_count = mh->ncmds;
#elif __ARM_ARCH_7S__
sHostCPU = CPU_TYPE_ARM;
sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S;
+#elif __arm64e__
+ sHostCPU = CPU_TYPE_ARM64;
+ sHostCPUsubtype = CPU_SUBTYPE_ARM64_E;
+#elif __arm64__
+ sHostCPU = CPU_TYPE_ARM64;
+ sHostCPUsubtype = CPU_SUBTYPE_ARM64_V8;
#else
struct host_basic_info info;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
sHostCPUsubtype = info.cpu_subtype;
mach_port_deallocate(mach_task_self(), hostPort);
#if __x86_64__
- #if DYLD_SHARED_CACHE_SUPPORT
+ // host_info returns CPU_TYPE_I386 even for x86_64. Override that here so that
+ // we don't need to mask the cpu type later.
+ sHostCPU = CPU_TYPE_X86_64;
+ #if !TARGET_IPHONE_SIMULATOR
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 ) {
#endif
}
-static void checkSharedRegionDisable()
+static void checkSharedRegionDisable(const mach_header* mainExecutableMH)
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- // if main executable has segments that overlap the shared region,
+ // if main executable has segments that overlap the shared region,
// then disable using the shared region
- if ( sMainExecutable->overlapsWithAddressRange((void*)(uintptr_t)SHARED_REGION_BASE, (void*)(uintptr_t)(SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) {
+ dyld3::MachOParser parser(mainExecutableMH);
+ uintptr_t slide = parser.getSlide();
+ dyld3::launch_cache::MemoryRange sharedRegion = { (void*)(long)(SHARED_REGION_BASE), SHARED_REGION_SIZE };
+ __block bool disable = false;
+ parser.forEachSegment(^(const char *segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) {
+ dyld3::launch_cache::MemoryRange segRegion = { (void*)(long)(vmAddr+slide), vmSize };
+ if ( segRegion.intersects(sharedRegion) )
+ disable = true;
+ });
+ if ( disable ) {
gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
if ( gLinkContext.verboseMapping )
dyld::warn("disabling shared region because main executable overlaps\n");
}
#endif
#endif
- // iPhoneOS cannot run without shared region
+ // iOS cannot run without shared region
}
bool validImage(const ImageLoader* possibleImage)
};
#endif
+#if __arm64__
+//
+// ARM64 sub-type lists
+//
+const int kARM64_RowCount = 2;
+static const cpu_subtype_t kARM64[kARM64_RowCount][4] = {
+
+ // armv64e can run: 64e, 64
+ { CPU_SUBTYPE_ARM64_E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+
+ // armv64 can run: 64
+ { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+};
+#endif
+
#if __x86_64__
//
// x86_64 sub-type lists
static const cpu_subtype_t kX86_64[kX86_64_RowCount][5] = {
// x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64
- { CPU_SUBTYPE_X86_64_H, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST },
+ { CPU_SUBTYPE_X86_64_H, (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H), (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL), CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST },
// x86_64 can run: x86_64(lib) and x86_64
- { CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST },
+ { CPU_SUBTYPE_X86_64_ALL, (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL), CPU_SUBTYPE_END_OF_LIST },
};
#endif
}
break;
#endif
+#if __arm64__
+ case CPU_TYPE_ARM64:
+ for (int i=0; i < kARM64_RowCount ; ++i) {
+ if ( kARM64[i][0] == subtype )
+ return kARM64[i];
+ }
+ break;
+#endif
#if __x86_64__
case CPU_TYPE_X86_64:
for (int i=0; i < kX86_64_RowCount ; ++i) {
}
break;
#endif
+#if __arm64__
+ case CPU_TYPE_ARM64:
+ if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM64_ALL ) {
+ *offset = OSSwapBigToHostInt32(archs[i].offset);
+ *len = OSSwapBigToHostInt32(archs[i].size);
+ return true;
+ }
+ break;
+#endif
#if __x86_64__
case CPU_TYPE_X86_64:
if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) {
const cpu_type_t cpu = sMainExecutableMachHeader->cputype;
// We only know the subtype to use if the main executable cpu type matches the host
- if ( (cpu & CPU_TYPE_MASK) == sHostCPU ) {
+ if ( cpu == sHostCPU ) {
// get preference ordered list of subtypes
const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype);
const mach_header* mh = (mach_header*)firstPage;
if ( mh->magic == sMainExecutableMachHeader->magic ) {
if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
- if ( (mh->cputype & CPU_TYPE_MASK) == sHostCPU ) {
+ if ( mh->cputype == sHostCPU ) {
// get preference ordered list of subtypes that this machine can use
const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
if ( subTypePreferenceList != NULL ) {
throw "main executable not a known format";
}
-#if DYLD_SHARED_CACHE_SUPPORT
-
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if SUPPORT_ACCELERATE_TABLES
static bool dylibsCanOverrideCache()
{
- uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
- if ( (devFlags & 1) == 0 )
+ if ( !dyld3::loader::internalInstall() )
return false;
- return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) );
+ return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.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)
+const void* imMemorySharedCacheHeader()
{
- if ( sSharedCache != NULL ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- // Mac OS X always requires inode/mtime to valid cache
- // if stat() not done yet, do it now
- struct stat statb;
- if ( stat_buf == NULL ) {
- if ( my_stat(path, &statb) == -1 )
- return false;
- stat_buf = &statb;
- }
-#endif
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- uint64_t hash = 0;
- for (const char* s=path; *s != '\0'; ++s)
- hash += hash*4 + *s;
-#endif
+ return sSharedCacheLoadInfo.loadAddress;
+}
- // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired
- const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset);
- const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount];
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- const bool cacheHasHashInfo = (start->modTime == 0);
-#endif
- for( const dyld_cache_image_info* p = start; p != end; ++p) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- // just check path
- const char* aPath = (char*)sSharedCache + p->pathFileOffset;
- if ( cacheHasHashInfo && (p->inode != hash) )
- continue;
- if ( strcmp(path, aPath) == 0 ) {
- // found image in cache
- *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
- // check mtime and inode first because it is fast
- bool inodeMatch = ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) );
- if ( searchByPath || sSharedCacheIgnoreInodeAndTimeStamp || inodeMatch ) {
- // mod-time and inode match an image in the shared cache, now check path
- const char* aPath = (char*)sSharedCache + p->pathFileOffset;
- bool cacheHit = (strcmp(path, aPath) == 0);
- if ( inodeMatch && !cacheHit ) {
- // path does not match install name of dylib in cache, but inode and mtime does match
- // perhaps path is a symlink to the cached dylib
- struct stat pathInCacheStatBuf;
- if ( my_stat(aPath, &pathInCacheStatBuf) != -1 )
- cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf->st_dev) && (pathInCacheStatBuf.st_ino == stat_buf->st_ino) );
- }
- 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=%s\n",
- // *mh, p->address, sSharedCacheSlide, aPath);
- *pathInCache = aPath;
- *slide = sSharedCacheSlide;
- return true;
- }
- }
-#endif
- }
+
+const char* getStandardSharedCacheFilePath()
+{
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr )
+ return sSharedCacheLoadInfo.path;
+ else
+ return nullptr;
+}
+
+
+static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
+{
+ dyld3::SharedCacheFindDylibResults results;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &results) ) {
+ *mh = (macho_header*)results.mhInCache;
+ *pathInCache = results.pathInCache;
+ *slide = results.slideInCache;
+ return true;
}
return false;
}
bool inSharedCache(const char* path)
{
- const macho_header* mhInCache;
- const char* pathInCache;
- long slide;
- return findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slide);
+ return dyld3::pathIsInSharedCacheImage(sSharedCacheLoadInfo, path);
}
-#endif
static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context)
{
// min mach-o file is 4K
if ( fileLength < 4096 ) {
- if ( pread(fd, firstPages, fileLength, 0) != (ssize_t)fileLength )
+ if ( pread(fd, firstPages, (size_t)fileLength, 0) != (ssize_t)fileLength )
throwf("pread of short file failed: %d", errno);
shortPage = true;
}
}
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-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 ) {
- 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 )
- return image;
-
- // do nothing if not already loaded and if RTLD_NOLOAD or NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
- if ( context.dontLoad )
- return NULL;
-
-#if DYLD_SHARED_CACHE_SUPPORT
- // see if this image is in shared cache
- const macho_header* mhInCache;
- const char* pathInCache;
- long slideInCache;
- if ( findInSharedCacheImage(path, false, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) {
- image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext);
- return checkandAddImage(image, context);
- }
-#endif
- // file exists and is not in dyld shared cache, so open it
- return loadPhase5open(path, context, stat_buf, exceptions);
-}
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
-
-
-
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-static ImageLoader* loadPhase5stat(const char* path, const LoadContext& context, struct stat* stat_buf,
- int* statErrNo, bool* imageFound, std::vector<const char*>* exceptions)
-{
- ImageLoader* image = NULL;
- *imageFound = false;
- *statErrNo = 0;
- if ( my_stat(path, stat_buf) == 0 ) {
- // in case image was renamed or found via symlinks, check for inode match
- image = findLoadedImage(*stat_buf);
- if ( image != NULL ) {
- *imageFound = true;
- return image;
- }
- // do nothing if not already loaded and if RTLD_NOLOAD
- if ( context.dontLoad ) {
- *imageFound = true;
- return NULL;
- }
- image = loadPhase5open(path, context, *stat_buf, exceptions);
- if ( image != NULL ) {
- *imageFound = true;
- return image;
- }
- }
- else {
- *statErrNo = errno;
- }
- return NULL;
-}
// try to open file
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;
- bool imageFound;
- int statErrNo;
- ImageLoader* image;
-#if DYLD_SHARED_CACHE_SUPPORT
- #if SUPPORT_ACCELERATE_TABLES
+#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 image;
- }
- // see if this image is in shared cache
- const macho_header* mhInCache;
- const char* pathInCache;
- long slideInCache;
- if ( findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) {
+#endif
+#if TARGET_IPHONE_SIMULATOR
+ // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
+ const char* pathToFindInCache = orgPath;
+#else
+ const char* pathToFindInCache = path;
+#endif
+ uint statErrNo;
+ struct stat statBuf;
+ bool didStat = false;
+ bool existsOnDisk;
+ dyld3::SharedCacheFindDylibResults shareCacheResults;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) {
// see if this image in the cache was already loaded via a different path
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
ImageLoader* anImage = *it;
- if ( (const macho_header*)anImage->machHeader() == mhInCache )
+ if ( (const mach_header*)anImage->machHeader() == shareCacheResults.mhInCache )
return anImage;
}
- // do nothing if not already loaded and if RTLD_NOLOAD
+ // if RTLD_NOLOAD, do nothing if not already loaded
if ( context.dontLoad )
return NULL;
- // nope, so instantiate a new image from dyld shared cache
- // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
- bzero(&stat_buf, sizeof(stat_buf));
- image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext);
- return checkandAddImage(image, context);
+ bool useCache = false;
+ if ( shareCacheResults.imageData == nullptr ) {
+ // HACK to support old caches
+ existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+ didStat = true;
+ statErrNo = errno;
+ useCache = !existsOnDisk;
+ }
+ else {
+ // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
+ bzero(&statBuf, sizeof(statBuf));
+ dyld3::launch_cache::Image image(shareCacheResults.imageData);
+ if ( image.overridableDylib() ) {
+ existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+ didStat = true;
+ statErrNo = errno;
+ if ( sSharedCacheLoadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
+ if ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) )
+ useCache = true;
+ }
+ else {
+ if ( !existsOnDisk )
+ useCache = true;
+ }
+ }
+ else {
+ useCache = true;
+ }
+ }
+ if ( useCache ) {
+ ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
+ return checkandAddImage(imageLoader, context);
+ }
}
-
- 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 )
- return image;
+
+ // not in cache or cache not usable
+ if ( !didStat ) {
+ existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+ statErrNo = errno;
}
-#else
- image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
- if ( imageFound )
- return image;
-#endif
+ if ( existsOnDisk ) {
+ // in case image was renamed or found via symlinks, check for inode match
+ ImageLoader* imageLoader = findLoadedImage(statBuf);
+ if ( imageLoader != NULL )
+ return imageLoader;
+ // do nothing if not already loaded and if RTLD_NOLOAD
+ if ( context.dontLoad )
+ return NULL;
+ // try opening file
+ imageLoader = loadPhase5open(path, context, statBuf, exceptions);
+ if ( imageLoader != NULL )
+ return imageLoader;
+ }
+
// just return NULL if file not found, but record any other errors
if ( (statErrNo != ENOENT) && (statErrNo != 0) ) {
if ( (statErrNo == EPERM) && sandboxBlockedStat(path) )
}
return NULL;
}
-#endif // __IPHONE_OS_VERSION_MIN_REQUIRED
-
// look for path match with existing loaded images
static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context)
return loadPhase1(path, orgPath, context, cacheIndex, exceptions);
}
-#if DYLD_SHARED_CACHE_SUPPORT
- static bool cacheablePath(const char* path) {
- if (strncmp(path, "/usr/lib/", 9) == 0)
- return true;
- if (strncmp(path, "/System/Library/", 16) == 0)
- return true;
- return false;
- }
-#endif
+static bool cacheablePath(const char* path) {
+ if (strncmp(path, "/usr/lib/", 9) == 0)
+ return true;
+ if (strncmp(path, "/System/Library/", 16) == 0)
+ return true;
+ return false;
+}
//
// Given all the DYLD_ environment variables, the general case for loading libraries
for (std::vector<const char*>::iterator it = exceptions.begin(); it != exceptions.end(); ++it) {
free((void*)(*it));
}
-#if DYLD_SHARED_CACHE_SUPPORT
// if loaded image is not from cache, but original path is in cache
// set gSharedCacheOverridden flag to disable some ObjC optimizations
if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) {
gSharedCacheOverridden = true;
}
-#endif
return image;
}
else if ( exceptions.size() == 0 ) {
-#if DYLD_SHARED_CACHE_SUPPORT
-
-#if __i386__
- #define ARCH_NAME "i386"
- #define ARCH_CACHE_MAGIC "dyld_v1 i386"
-#elif __x86_64__
- #define ARCH_NAME "x86_64"
- #define ARCH_CACHE_MAGIC "dyld_v1 x86_64"
- #define ARCH_NAME_H "x86_64h"
- #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h"
-#elif __ARM_ARCH_5TEJ__
- #define ARCH_NAME "armv5"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv5"
-#elif __ARM_ARCH_6K__
- #define ARCH_NAME "armv6"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv6"
-#elif __ARM_ARCH_7F__
- #define ARCH_NAME "armv7f"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv7f"
-#elif __ARM_ARCH_7K__
- #define ARCH_NAME "armv7k"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv7k"
-#elif __ARM_ARCH_7A__
- #define ARCH_NAME "armv7"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv7"
-#elif __ARM_ARCH_7S__
- #define ARCH_NAME "armv7s"
- #define ARCH_CACHE_MAGIC "dyld_v1 armv7s"
-#elif __arm64__
- #define ARCH_NAME "arm64"
- #define ARCH_CACHE_MAGIC "dyld_v1 arm64"
+static void mapSharedCache()
+{
+ dyld3::SharedCacheOptions opts;
+ opts.cacheDirOverride = sSharedCacheOverrideDir;
+ opts.forcePrivate = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion);
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
+ opts.useHaswell = sHaswell;
+#else
+ opts.useHaswell = false;
#endif
+ opts.verbose = gLinkContext.verboseMapping;
+ loadDyldCache(opts, &sSharedCacheLoadInfo);
+ // update global state
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate;
+ dyld::gProcessInfo->sharedCacheSlide = sSharedCacheLoadInfo.slide;
+ dyld::gProcessInfo->sharedCacheBaseAddress = (unsigned long)sSharedCacheLoadInfo.loadAddress;
+ sSharedCacheLoadInfo.loadAddress->getUUID(dyld::gProcessInfo->sharedCacheUUID);
+ dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress);
+ }
-static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address)
-{
- if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion )
- return syscall(294, start_address);
- return -1;
+//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+// RAM disk booting does not have shared cache yet
+// Don't make lack of a shared cache fatal in that case
+// if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+// if ( sSharedCacheLoadInfo.errorMessage != nullptr )
+// halt(sSharedCacheLoadInfo.errorMessage);
+// else
+// halt("error loading dyld shared cache");
+// }
+//#endif
}
-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;
- }
+// create when NSLinkModule is called for a second time on a bundle
+ImageLoader* cloneImage(ImageLoader* image)
+{
+ // open file (automagically closed when this function exits)
+ FileOpener file(image->getPath());
+
+ struct stat stat_buf;
+ if ( fstat(file.getFileDescriptor(), &stat_buf) == -1)
+ throw "stat error";
+
+ dyld::LoadContext context;
+ context.useSearchPaths = false;
+ context.useFallbackPaths = false;
+ context.useLdLibraryPath = false;
+ context.implicitRPath = false;
+ context.matchByInstallName = false;
+ context.dontLoad = false;
+ context.mustBeBundle = true;
+ context.mustBeDylib = false;
+ context.canBePIE = false;
+ context.origin = NULL;
+ context.rpath = NULL;
+ return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context);
}
-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)
+ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName)
{
- // register code signature blob for whole dyld cache
- 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 ) {
-#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);
- }
-
- // remove the shared region sub-map
- vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE);
-
- // notify gdb or other lurkers that this process is no longer using the shared region
- dyld::gProcessInfo->processDetachedFromSharedRegion = true;
-
- // map cache just for this process with mmap()
- const shared_file_mapping_np* const start = mappings;
- const shared_file_mapping_np* const end = &mappings[count];
- for (const shared_file_mapping_np* p = start; p < end; ++p ) {
- void* mmapAddress = (void*)(uintptr_t)(p->sfm_address);
- size_t size = p->sfm_size;
- //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size);
- int protection = 0;
- if ( p->sfm_init_prot & VM_PROT_EXECUTE )
- protection |= PROT_EXEC;
- if ( p->sfm_init_prot & VM_PROT_READ )
- protection |= PROT_READ;
- if ( p->sfm_init_prot & VM_PROT_WRITE )
- protection |= PROT_WRITE;
- off_t offset = p->sfm_file_offset;
- if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) {
- // failed to map some chunk of this shared cache file
- // clear shared region
- vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE);
- // go back to not using shared region at all
- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
- if ( gLinkContext.verboseMapping ) {
- dyld::log("dyld: shared cached region cannot be mapped at address %p with size 0x%08lX\n",
- mmapAddress, size);
- }
- // return failure
- return -1;
- }
- }
-
- // update all __DATA pages with slide info
- 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 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) {
- const uint8_t* entry = &entries[toc[i]*slideInfoHeader->entries_size];
- const uint8_t* page = (uint8_t*)(long)(dataPagesStart + (4096*i));
- //dyld::log("page=%p toc[%d]=%d entries=%p\n", page, i, toc[i], entry);
- for(int j=0; j < 128; ++j) {
- uint8_t b = entry[j];
- //dyld::log(" entry[%d] = 0x%02X\n", j, b);
- if ( b != 0 ) {
- for(int k=0; k < 8; ++k) {
- if ( b & (1<<k) ) {
- uintptr_t* p = (uintptr_t*)(page + j*8*4 + k*4);
- uintptr_t value = *p;
- //dyld::log(" *%p was 0x%lX will be 0x%lX\n", p, value, value+sSharedCacheSlide);
- *p = value + slide;
- }
- }
- }
- }
- }
- }
-
- // succesfully mapped shared cache for just this process
- gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
-
- return 0;
-}
-
-
-const void* imMemorySharedCacheHeader()
-{
- return sSharedCache;
-}
-
-const char* getStandardSharedCacheFilePath()
-{
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
-#else
- #if __x86_64__
- if ( sHaswell ) {
- const char* path2 = MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H;
- struct stat statBuf;
- if ( my_stat(path2, &statBuf) == 0 )
- return path2;
- }
- #endif
- return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
-#endif
-}
-
-int openSharedCacheFile()
-{
- char path[MAXPATHLEN];
- strlcpy(path, sSharedCacheDir, MAXPATHLEN);
- strlcat(path, "/", MAXPATHLEN);
-#if __x86_64__
- if ( sHaswell ) {
- strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, MAXPATHLEN);
- int fd = my_open(path, O_RDONLY, 0);
- if ( fd != -1 ) {
- if ( gLinkContext.verboseMapping )
- dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path);
- return fd;
- }
- strlcpy(path, sSharedCacheDir, MAXPATHLEN);
- }
-#endif
- strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN);
-#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);
-}
-
-
-static void getCacheBounds(uint32_t mappingsCount, const shared_file_mapping_np mappings[], uint64_t& lowAddress, uint64_t& highAddress)
-{
- lowAddress = 0;
- highAddress = 0;
- for(uint32_t i=0; i < mappingsCount; ++i) {
- if ( lowAddress == 0 ) {
- lowAddress = mappings[i].sfm_address;
- highAddress = mappings[i].sfm_address + mappings[i].sfm_size;
- }
- else {
- if ( mappings[i].sfm_address < lowAddress )
- lowAddress = mappings[i].sfm_address;
- if ( (mappings[i].sfm_address + mappings[i].sfm_size) > highAddress )
- highAddress = mappings[i].sfm_address + mappings[i].sfm_size;
- }
- }
-}
-
-static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[])
-{
-#if __x86_64__
- // x86_64 has a two memory regions:
- // 256MB at 0x00007FFF70000000
- // 1024MB at 0x00007FFF80000000
- // Some old shared caches have r/w region after rx region, so all regions slide within 1GB range
- // Newer shared caches have r/w region based at 0x7FFF70000000 and r/o regions at 0x7FFF80000000, so each part has max slide
- if ( (mappingsCount >= 3) && (mappings[1].sfm_init_prot == (VM_PROT_READ|VM_PROT_WRITE)) && (mappings[1].sfm_address == 0x00007FFF70000000) ) {
- const uint64_t rwSize = mappings[1].sfm_size;
- const uint64_t rwSlop = 0x10000000ULL - rwSize;
- const uint64_t roSize = (mappings[2].sfm_address + mappings[2].sfm_size) - mappings[0].sfm_address;
- const uint64_t roSlop = 0x40000000ULL - roSize;
- const uint64_t space = (rwSlop < roSlop) ? rwSlop : roSlop;
-
- // choose new random slide
- long slide = (arc4random() % space) & (-4096);
- //dyld::log("rwSlop=0x%0llX, roSlop=0x%0llX\n", rwSlop, roSlop);
- //dyld::log("space=0x%0llX, slide=0x%0lX\n", space, slide);
-
- // update mappings
- for(uint32_t i=0; i < mappingsCount; ++i) {
- mappings[i].sfm_address += slide;
- }
-
- return slide;
- }
- // else fall through to handle old style cache
-#endif
- // get bounds of cache
- uint64_t lowAddress;
- uint64_t highAddress;
- getCacheBounds(mappingsCount, mappings, lowAddress, highAddress);
-
- // find slop space
- 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;
- }
-
- return slide;
-}
-
-static void mapSharedCache()
-{
- uint64_t cacheBaseAddress = 0;
- // quick check if a cache is already mapped into shared region
- if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) {
- sSharedCache = (dyld_cache_header*)cacheBaseAddress;
- // if we don't understand the currently mapped shared cache, then ignore
-#if __x86_64__
- const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC);
-#else
- const char* magic = ARCH_CACHE_MAGIC;
-#endif
- if ( strcmp(sSharedCache->magic, magic) != 0 ) {
- sSharedCache = NULL;
- if ( gLinkContext.verboseMapping ) {
- dyld::log("dyld: existing shared cached in memory is not compatible\n");
- return;
- }
- }
- dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress;
- // check if cache file is slidable
- const dyld_cache_header* header = sSharedCache;
- if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) {
- // solve for slide by comparing loaded address to address of first region
- const uint8_t* loadedAddress = (uint8_t*)sSharedCache;
- const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset);
- const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address);
- sSharedCacheSlide = loadedAddress - preferedLoadAddress;
- dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide;
- //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress);
- }
- // if cache has a uuid, copy it
- if ( header->mappingOffset >= 0x68 ) {
- memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16);
- }
- // verbose logging
- if ( gLinkContext.verboseMapping ) {
- 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 __i386__ || __x86_64__
- // <rdar://problem/5925940> Safe Boot should disable dyld shared cache
- // if we are in safe-boot mode and the cache was not made during this boot cycle,
- // delete the cache file
- uint32_t safeBootValue = 0;
- size_t safeBootValueSize = sizeof(safeBootValue);
- if ( (sysctlbyname("kern.safeboot", &safeBootValue, &safeBootValueSize, NULL, 0) == 0) && (safeBootValue != 0) ) {
- // user booted machine in safe-boot mode
- struct stat dyldCacheStatInfo;
- // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path
- if ( my_stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) {
- struct timeval bootTimeValue;
- size_t bootTimeValueSize = sizeof(bootTimeValue);
- if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) {
- // if the cache file was created before this boot, then throw it away and let it rebuild itself
- if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) {
- ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME);
- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
- return;
- }
- }
- }
- }
-#endif
- // map in shared cache to shared region
- int fd = openSharedCacheFile();
- if ( fd != -1 ) {
- uint8_t firstPages[8192];
- if ( ::read(fd, firstPages, 8192) == 8192 ) {
- dyld_cache_header* header = (dyld_cache_header*)firstPages;
- #if __x86_64__
- const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC);
- #else
- const char* magic = ARCH_CACHE_MAGIC;
- #endif
- 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];
- #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 readWriteMappingIndex = -1;
- int readOnlyMappingIndex = -1;
- // validate that the cache file has not been truncated
- bool goodCache = false;
- struct stat stat_buf;
- if ( fstat(fd, &stat_buf) == 0 ) {
- goodCache = true;
- int i=0;
- for (const dyld_cache_mapping_info* p = fileMappingsStart; p < fileMappingsEnd; ++p, ++i) {
- mappings[i].sfm_address = p->address;
- mappings[i].sfm_size = p->size;
- mappings[i].sfm_file_offset = p->fileOffset;
- mappings[i].sfm_max_prot = p->maxProt;
- mappings[i].sfm_init_prot = p->initProt;
- // rdar://problem/5694507 old update_dyld_shared_cache tool could make a cache file
- // that is not page aligned, but otherwise ok.
- if ( p->fileOffset+p->size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) {
- dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
- goodCache = false;
- }
- if ( (mappings[i].sfm_init_prot & (VM_PROT_READ|VM_PROT_WRITE)) == (VM_PROT_READ|VM_PROT_WRITE) ) {
- readWriteMappingIndex = i;
- }
- if ( mappings[i].sfm_init_prot == VM_PROT_READ ) {
- readOnlyMappingIndex = i;
- }
- }
- // if shared cache is code signed, add a mapping for the code signature
- uint64_t signatureSize = header->codeSignatureSize;
- // zero size in header means signature runs to end-of-file
- if ( signatureSize == 0 )
- signatureSize = stat_buf.st_size - header->codeSignatureOffset;
- if ( signatureSize != 0 ) {
-#if __arm__ || __arm64__
- size_t alignedSignatureSize = (signatureSize+16383) & (-16384);
-#else
- size_t alignedSignatureSize = (signatureSize+4095) & (-4096);
-#endif
- // <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 ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) {
- bool foundLibSystem = false;
- if ( my_stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) {
- const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset];
- const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount];
- for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) {
- if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) {
- foundLibSystem = true;
- break;
- }
- }
- }
- if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) {
- dyld::log("dyld: shared cached file was built against a different libSystem.dylib, ignoring cache.\n"
- "to update dyld shared cache run: 'sudo update_dyld_shared_cache' then reboot.\n");
- goodCache = false;
- }
- }
-#endif
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
- {
- uint64_t lowAddress;
- uint64_t highAddress;
- getCacheBounds(mappingCount, mappings, lowAddress, highAddress);
- if ( (highAddress-lowAddress) > SHARED_REGION_SIZE )
- throw "dyld shared cache is too big to fit in shared region";
- }
-#endif
-
- if ( goodCache && (readWriteMappingIndex == -1) ) {
- dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
- goodCache = false;
- }
- if ( goodCache && (readOnlyMappingIndex == -1) ) {
- dyld::log("dyld: shared cached file is missing read-only mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
- goodCache = false;
- }
- if ( goodCache ) {
- long cacheSlide = 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 ( slideInfoSize != 0 ) {
- // <rdar://problem/8611968> don't slide shared cache if ASLR disabled (main executable didn't slide)
- if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) ) {
- cacheSlide = 0;
- }
- else {
- // generate random slide amount
- cacheSlide = pickCacheSlide(mappingCount, mappings);
- }
-
- 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");
- for (int i=0; i < mappingCount; ++i) {
- 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, 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
- 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");
- }
- }
- }
- else {
- if ( gLinkContext.verboseMapping )
- dyld::log("dyld: shared cached file is invalid\n");
- }
- }
- else {
- if ( gLinkContext.verboseMapping )
- dyld::log("dyld: shared cached file cannot be read\n");
- }
- close(fd);
- }
- else {
- if ( gLinkContext.verboseMapping )
- dyld::log("dyld: shared cached file cannot be opened\n");
- }
- }
-
- // remember if dyld loaded at same address as when cache built
- if ( sSharedCache != NULL ) {
- gLinkContext.dyldLoadedAtSameAddressNeededBySharedCache = ((uintptr_t)(sSharedCache->dyldBaseAddress) == (uintptr_t)&_mh_dylinker_header);
- }
-
- // tell gdb where the shared cache is
- if ( sSharedCache != NULL ) {
- const dyld_cache_mapping_info* const start = (dyld_cache_mapping_info*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset);
- dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount;
- // only room to tell gdb about first four regions
- if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 )
- dyld_shared_cache_ranges.sharedRegionsCount = 4;
- const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount];
- int index = 0;
- for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) {
- dyld_shared_cache_ranges.ranges[index].start = p->address+sSharedCacheSlide;
- dyld_shared_cache_ranges.ranges[index].length = p->size;
- if ( gLinkContext.verboseMapping ) {
- dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n",
- p->address+sSharedCacheSlide, p->address+sSharedCacheSlide+p->size-1,
- ((p->initProt & VM_PROT_READ) ? "read " : ""),
- ((p->initProt & VM_PROT_WRITE) ? "write " : ""),
- ((p->initProt & VM_PROT_EXECUTE) ? "execute " : ""), p->initProt, p->maxProt);
- }
- #if __i386__
- // If a non-writable and executable region is found in the R/W shared region, then this is __IMPORT segments
- // This is an old cache. Make writable. dyld no longer supports turn W on and off as it binds
- if ( (p->initProt == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->address & 0xF0000000) == 0xA0000000) ) {
- if ( p->size != 0 ) {
- vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ | VM_PROT_WRITE;
- vm_protect(mach_task_self(), p->address, p->size, false, prot);
- if ( gLinkContext.verboseMapping ) {
- dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->address,
- p->address+p->size-1,
- (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' );
- }
- }
- }
- #endif
- }
- if ( gLinkContext.verboseMapping ) {
- // list the code blob
- dyld_cache_header* header = (dyld_cache_header*)sSharedCache;
- uint64_t signatureSize = header->codeSignatureSize;
- // zero size in header means signature runs to end-of-file
- if ( signatureSize == 0 ) {
- struct stat stat_buf;
- // FIXME: need size of cache file actually used
- if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 )
- signatureSize = stat_buf.st_size - header->codeSignatureOffset;
- }
- if ( signatureSize != 0 ) {
- const dyld_cache_mapping_info* const last = &start[dyld_shared_cache_ranges.sharedRegionsCount-1];
- uint64_t codeBlobStart = last->address + last->size;
- dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize);
- }
- }
- #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
-
-
-
-// create when NSLinkModule is called for a second time on a bundle
-ImageLoader* cloneImage(ImageLoader* image)
-{
- // open file (automagically closed when this function exits)
- FileOpener file(image->getPath());
-
- struct stat stat_buf;
- if ( fstat(file.getFileDescriptor(), &stat_buf) == -1)
- throw "stat error";
-
- dyld::LoadContext context;
- context.useSearchPaths = false;
- context.useFallbackPaths = false;
- context.useLdLibraryPath = false;
- context.implicitRPath = false;
- context.matchByInstallName = false;
- context.dontLoad = false;
- context.mustBeBundle = true;
- context.mustBeDylib = false;
- context.canBePIE = false;
- context.origin = NULL;
- context.rpath = NULL;
- return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context);
-}
-
-
-ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName)
-{
- // if fat wrapper, find usable sub-file
- const fat_header* memStartAsFat = (fat_header*)mem;
- uint64_t fileOffset = 0;
- uint64_t fileLength = len;
- if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) {
- mem = &mem[fileOffset];
- len = fileLength;
- }
- else {
- throw "no matching architecture in universal wrapper";
- }
+ // if fat wrapper, find usable sub-file
+ const fat_header* memStartAsFat = (fat_header*)mem;
+ uint64_t fileOffset = 0;
+ uint64_t fileLength = len;
+ if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) {
+ mem = &mem[fileOffset];
+ len = fileLength;
+ }
+ else {
+ throw "no matching architecture in universal wrapper";
+ }
}
// try each loader
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);
+ (*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide);
}
}
#endif
catch (const char* msg) {
// ignore request to abort during registration
}
+
+ // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
+ for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
+ ImageLoader* image = *it;
+ if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
+ (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
+ }
+ }
}
bool sharedCacheUUID(uuid_t uuid)
{
-#if DYLD_SHARED_CACHE_SUPPORT
- if ( sSharedCache == NULL )
+ if ( sSharedCacheLoadInfo.loadAddress == nullptr )
return false;
- memcpy(uuid, sSharedCache->uuid, 16);
+ sSharedCacheLoadInfo.loadAddress->getUUID(uuid);
return true;
-#else
- return false;
-#endif
}
#if SUPPORT_ACCELERATE_TABLES
gLinkContext.printAllDepths = &printAllDepths;
gLinkContext.imageCount = &imageCount;
gLinkContext.setNewProgramVars = &setNewProgramVars;
-#if DYLD_SHARED_CACHE_SUPPORT
gLinkContext.inSharedCache = &inSharedCache;
-#endif
gLinkContext.setErrorStrings = &setErrorStrings;
#if SUPPORT_OLD_CRT_INITIALIZATION
gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay;
gLinkContext.dynamicInterposeCount = 0;
gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding;
#if TARGET_IPHONE_SIMULATOR
- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+ gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
#else
gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion;
#endif
// Its presences means that the binary wants to have DYLD ignore
// DYLD_ environment variables.
//
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
static bool hasRestrictedSegment(const macho_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
return false;
}
+#endif
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
static bool isFairPlayEncrypted(const macho_header* mh)
{
const uint32_t cmd_count = mh->ncmds;
uint8_t firstPage[4096];
const macho_header* mh = (macho_header*)firstPage;
if ( !readFirstPage(dylibPath, firstPage) ) {
- #if DYLD_SHARED_CACHE_SUPPORT
// If file cannot be read, check to see if path is in shared cache
const macho_header* mhInCache;
const char* pathInCache;
if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) )
return false;
mh = mhInCache;
- #else
- return false;
- #endif
}
// check mach-o header
//
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;
+ uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
if ( flags & CS_ENFORCEMENT ) {
if ( flags & CS_GET_TASK_ALLOW ) {
gLinkContext.processIsRestricted = true;
}
bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
+ uint32_t flags;
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) && usingSIP ) {
}
}
-void notifyKernelAboutDyld()
+void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo)
{
- const struct macho_header* mh = (macho_header*)&__dso_handle;
+ const char *endptr = nullptr;
+ uint64_t fsid_scalar = hexToUInt64(fileInfo, &endptr);
+ uint64_t fsobj_id_scalar = 0;
+ if (endptr != nullptr) {
+ fsobj_id_scalar = hexToUInt64(endptr+1, &endptr);
+ }
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;
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);
+ dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, (const uuid_t *)&uc->uuid[0], *reinterpret_cast<fsobj_id_t *>(&fsobj_id_scalar), *reinterpret_cast<fsid_t *>(&fsid_scalar), (const mach_header *)mh);
return;
}
}
typedef int (*ioctl_proc_t)(int, unsigned long, void*);
static void* getProcessInfo() { return dyld::gProcessInfo; }
static SyscallHelpers sSysCalls = {
- 7,
+ 8,
// added in version 1
(open_proc_t)&open,
&close,
&task_get_dyld_image_infos,
&task_register_dyld_shared_cache_image_info,
&task_register_dyld_set_dyld_state,
- &task_register_dyld_get_process_state
+ &task_register_dyld_get_process_state,
+ // Added in version 8
+ &task_info,
+ &thread_info,
+ &kdebug_is_enabled,
+ &kdebug_trace
};
__attribute__((noinline))
return "pread(dyld_sim) failed";
}
else if ( !isCompatibleMachO(firstPage, dyldPath) ) {
- return "dyld_sim not compatible mach-o";
+ return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch";
}
// calculate total size of dyld segments
if ( siginfo.fs_file_start < codeSigCmd->dataoff )
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;
cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header));
}
#endif
+//
+// If the DYLD_SKIP_MAIN environment is set to 1, dyld will return the
+// address of this function instead of main() in the target program which
+// __dyld_start jumps to. Useful for qualifying dyld itself.
+//
+int
+fake_main()
+{
+ return 0;
+}
+
+
+
+
+static bool envVarMatches(dyld3::launch_cache::Closure mainClosure, const char* envp[], const char* varName)
+{
+ __block const char* valueFromClosure = nullptr;
+ mainClosure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+ size_t keyLen = strlen(varName);
+ if ( (strncmp(varName, keyEqualValue, keyLen) == 0) && (keyEqualValue[keyLen] == '=') ) {
+ valueFromClosure = &keyEqualValue[keyLen+1];
+ stop = true;
+ }
+ });
+
+ const char* valueFromEnv = _simple_getenv(envp, varName);
+
+ bool inClosure = (valueFromClosure != nullptr);
+ bool inEnv = (valueFromEnv != nullptr);
+ if ( inClosure != inEnv )
+ return false;
+ if ( !inClosure && !inEnv )
+ return true;
+ return ( strcmp(valueFromClosure, valueFromEnv) == 0 );
+}
+
+static const char* const sEnvVarsToCheck[] = {
+ "DYLD_LIBRARY_PATH",
+ "DYLD_FRAMEWORK_PATH",
+ "DYLD_FALLBACK_LIBRARY_PATH",
+ "DYLD_FALLBACK_FRAMEWORK_PATH",
+ "DYLD_INSERT_LIBRARIES",
+ "DYLD_IMAGE_SUFFIX",
+ "DYLD_VERSIONED_FRAMEWORK_PATH",
+ "DYLD_VERSIONED_LIBRARY_PATH",
+ "DYLD_ROOT_PATH"
+};
+
+static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* envp[])
+{
+ for (const char* envVar : sEnvVarsToCheck) {
+ if ( !envVarMatches(mainClosure, envp, envVar) ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because %s changed\n", mainClosure.binaryData(), envVar);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool closureValid(const dyld3::launch_cache::BinaryClosureData* mainClosureData, const mach_header* mainExecutableMH, const uint8_t* mainExecutableCDHash, bool closureInCache, const char* envp[])
+{
+ const dyld3::launch_cache::Closure mainClosure(mainClosureData);
+ const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group();
+
+ // verify current dyld cache is same as expected
+ if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosureData);
+ return false;
+ }
+ if ( !closureInCache ) {
+ // closures in cache don't have cache's UUID
+ uuid_t cacheUUID;
+ sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+ if ( memcmp(mainClosure.dyldCacheUUID(), cacheUUID, sizeof(uuid_t)) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosureData);
+ return false;
+ }
+ }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ else {
+ // HACK until closured for dlopen can run against live cache file
+ int fd = my_open(sSharedCacheLoadInfo.path, O_RDONLY, 0);
+ if ( fd != -1 ) {
+ dyld_cache_header fileHeader;
+ if ( pread(fd, &fileHeader, sizeof(fileHeader), 0) == sizeof(fileHeader) ) {
+ uuid_t cacheUUID;
+ sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+ if ( memcmp(fileHeader.uuid, cacheUUID, sizeof(uuid_t)) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because current cache on disk is not they one being used\n", mainClosureData);
+ ::close(fd);
+ return false;
+ }
+ }
+ ::close(fd);
+ }
+ }
+#endif
+
+ // verify main executable file has not changed since closure was built
+ const dyld3::launch_cache::Image mainImage = mainGroup.image(mainClosure.mainExecutableImageIndex());
+ if ( mainImage.validateUsingModTimeAndInode() ) {
+ struct stat statBuf;
+ if ( ::stat(mainImage.path(), &statBuf) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because stat() failed on main executable\n", mainClosureData);
+ return false;
+ }
+ else if ( (statBuf.st_mtime != mainImage.fileModTime()) || (statBuf.st_ino != mainImage.fileINode()) ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because mtime/inode changed since closure was built\n", mainClosureData);
+ return false;
+ }
+ }
+
+ // verify cdHash of main executable is same as recorded in closure
+ if ( mainImage.validateUsingCdHash() ) {
+ if ( mainExecutableCDHash == nullptr ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosureData);
+ return false;
+ }
+ if ( memcmp(mainExecutableCDHash, mainClosure.cdHash(), 20) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosureData);
+ return false;
+ }
+ }
+
+ // verify UUID of main executable is same as recorded in closure
+ const uuid_t* closureMainUUID = mainImage.uuid();
+ dyld3::MachOParser parser(mainExecutableMH);
+ uuid_t actualUUID;
+ parser.getUuid(actualUUID);
+ if ( memcmp(actualUUID, closureMainUUID, sizeof(uuid_t)) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosureData);
+ return false;
+ }
+
+ // verify DYLD_* env vars are same as when closure was built
+ if ( !envVarsMatch(mainClosure, envp) ) {
+ return false;
+ }
+
+ // verify files that are supposed to be missing actually are missing
+ __block bool foundFileThatInvalidatesClosure = false;
+ mainClosure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+ struct stat statBuf;
+ if ( ::stat(path, &statBuf) == 0 ) {
+ stop = true;
+ foundFileThatInvalidatesClosure = true;
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosureData, path);
+ }
+ });
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // verify no key frameworks have been overridden since cache was built
+ if ( dyld3::loader::internalInstall() ) {
+ dyld3::loader::forEachLineInFile("/AppleInternal/Library/Preferences/dyld-potential-framework-overrides", ^(const char* path, bool& stop) {
+ dyld3::SharedCacheFindDylibResults shareCacheResults;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) ) {
+ dyld3::launch_cache::Image image(shareCacheResults.imageData);
+ struct stat statBuf;
+ if ( ::stat(path, &statBuf) == 0 ) {
+ if ( (image.fileModTime() != statBuf.st_mtime) || (image.fileINode() != statBuf.st_ino)) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because framework has changed: '%s'\n", mainClosureData, path);
+ foundFileThatInvalidatesClosure = true;
+ stop = true;
+ }
+ }
+ }
+ });
+ }
+#endif
+
+ return !foundFileThatInvalidatesClosure;
+}
+
+static bool nolog(const char* format, ...)
+{
+ return false;
+}
+
+static bool dolog(const char* format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ vlog(format, list);
+ va_end(list);
+ return true;
+}
+
+static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* mainClosureData,
+ const DyldSharedCache* dyldCache,
+ const mach_header* mainExecutableMH, uintptr_t mainExecutableSlide,
+ int argc, const char* argv[], const char* envp[], const char* apple[],
+ uintptr_t* entry, uintptr_t* startGlue)
+{
+ dyld3::launch_cache::Closure mainClosure(mainClosureData);
+ const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group();
+ const uint32_t mainExecutableIndex = mainClosure.mainExecutableImageIndex();
+ const dyld3::launch_cache::Image mainImage = mainGroup.image(mainExecutableIndex);
+ const uint32_t loadedImageCount = mainClosure.initialImageCount();
+
+ // construct array of groups
+ dyld3::DyldCacheParser cacheParser(dyldCache, false);
+ STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+ theGroups[0] = cacheParser.cachedDylibsGroup();
+ theGroups[1] = cacheParser.otherDylibsGroup();
+ theGroups[2] = mainClosure.group().binaryData();
+
+ // construct array of all Image*, starting with any inserted dylibs, then main executable
+ const dyld3::launch_cache::BinaryImageData* images[loadedImageCount];
+ dyld3::launch_cache::SlowLoadSet imageSet(&images[0], &images[loadedImageCount]);
+ for (uint32_t i=0; i <= mainExecutableIndex; ++i) {
+ imageSet.add(mainGroup.image(i).binaryData());
+ }
+ // add all dependents of main executable
+ if ( !mainImage.recurseAllDependentImages(theGroups, imageSet, nullptr) ) {
+ dyld::log("initial image list overflow, expected only %d\n", loadedImageCount);
+ return false;
+ }
+ // add dependents of any inserted dylibs
+ for (uint32_t i=0; i < mainExecutableIndex; ++i) {
+ if ( !mainGroup.image(i).recurseAllDependentImages(theGroups, imageSet, nullptr) ) {
+ dyld::log("initial image list overflow in inserted libraries, expected only %d\n", loadedImageCount);
+ return false;
+ }
+ }
+ const uint32_t actualImageCount = (uint32_t)imageSet.count();
+ // construct array of allImages
+ STACK_ALLOC_DYNARRAY(dyld3::loader::ImageInfo, actualImageCount, allImages);
+ for (int i=0; i < actualImageCount; ++i) {
+ dyld3::launch_cache::Image img(images[i]);
+ dyld3::launch_cache::ImageGroup grp = img.group();
+ allImages[i].imageData = img.binaryData();
+ allImages[i].loadAddress = nullptr;
+ allImages[i].groupNum = grp.groupNum();
+ allImages[i].indexInGroup = grp.indexInGroup(img.binaryData());
+ allImages[i].previouslyFixedUp = false;
+ allImages[i].justMapped = false;
+ allImages[i].justUsedFromDyldCache = false;
+ allImages[i].neverUnload = false;
+ }
+ // prefill address of main executable to mark it is already loaded
+ allImages[mainExecutableIndex].loadAddress = mainExecutableMH;
+
+ // map new images and apply all fixups
+ Diagnostics diag;
+ mapAndFixupImages(diag, allImages, (const uint8_t*)dyldCache, (gLinkContext.verboseLoading ? &dolog : &nolog),
+ (gLinkContext.verboseMapping ? &dolog : &nolog),
+ (gLinkContext.verboseBind ? &dolog : &nolog),
+ (gLinkContext.verboseDOF ? &dolog : &nolog));
+ if ( diag.hasError() ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: %s\n", diag.errorMessage());
+ return false;
+ }
+
+ //dyld::log("loaded image list:\n");
+ //for (int i=0; i < allImages.count(); ++i) {
+ // dyld3::launch_cache::Image img(allImages[i].imageData);
+ // dyld::log("binImage[%d]=%p, mh=%p, path=%s\n", i, allImages[i].imageData, allImages[i].loadAddress, img.path());
+ //}
+
+ // find special images
+ const dyld3::launch_cache::BinaryImageData* libSystemImage = mainClosure.libSystem(theGroups);
+ const dyld3::launch_cache::BinaryImageData* libDyldImage = mainClosure.libDyld(theGroups);
+ const mach_header* libdyldMH = nullptr;
+ const mach_header* libSystemMH = nullptr;
+ for (int i=0; i < allImages.count(); ++i) {
+ if ( allImages[i].imageData == libSystemImage )
+ libSystemMH = allImages[i].loadAddress;
+ else if ( allImages[i].imageData == libDyldImage )
+ libdyldMH = allImages[i].loadAddress;
+ }
+
+ // send info on all images to libdyld.dylb
+ const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)libdyldMH + mainClosure.libdyldVectorOffset());
+ libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple);
+ libDyldEntry->setHaltFunction(&halt);
+ if ( libDyldEntry->vectorVersion > 2 )
+ libDyldEntry->setChildForkFunction(&_dyld_fork_child);
+#if !TARGET_IPHONE_SIMULATOR
+ if ( libDyldEntry->vectorVersion > 3 )
+ libDyldEntry->setLogFunction(&dyld::vlog);
+#endif
+ libDyldEntry->setOldAllImageInfo(gProcessInfo);
+ libDyldEntry->setInitialImageList(mainClosureData, dyldCache, sSharedCacheLoadInfo.path, allImages, libSystemMH, libSystemImage);
+ // run initializers
+ CRSetCrashLogMessage("dyld3: launch, running initializers");
+ libDyldEntry->runInitialzersBottomUp((mach_header*)mainExecutableMH);
+ //dyld::log("returned from runInitialzersBottomUp()\n");
+
+ dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+ if ( mainClosure.mainExecutableUsesCRT() ) {
+ // old style app linked with crt1.o
+ // entry is "start" function in program
+ *startGlue = 0;
+ *entry = (uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset();
+ }
+ else {
+ // modern app with LC_MAIN
+ // set startGlue to "start" function in libdyld.dylib
+ // set entry to "main" function in program
+ *startGlue = (uintptr_t)(libDyldEntry->startFunc);
+ *entry =(uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset();
+ }
+ CRSetCrashLogMessage(NULL);
+ return true;
+}
+
+static void putHexNibble(uint8_t value, char*& p)
+{
+ if ( value < 10 )
+ *p++ = '0' + value;
+ else
+ *p++ = 'A' + value - 10;
+}
+
+static void putHexByte(uint8_t value, char*& p)
+{
+ value &= 0xFF;
+ putHexNibble(value >> 4, p);
+ putHexNibble(value & 0x0F, p);
+}
+
+static void makeHexLong(unsigned long value, char* p)
+{
+ *p++ = '0';
+ *p++ = 'x';
+#if __LP64__
+ putHexByte(value >> 56, p);
+ putHexByte(value >> 48, p);
+ putHexByte(value >> 40, p);
+ putHexByte(value >> 32, p);
+#endif
+ putHexByte(value >> 24, p);
+ putHexByte(value >> 16, p);
+ putHexByte(value >> 8, p);
+ putHexByte(value, p);
+ *p = '\0';
+}
+
+static void makeUUID(uint8_t uuid[16], char* p)
+{
+ putHexByte(uuid[0], p);
+ putHexByte(uuid[1], p);
+ putHexByte(uuid[2], p);
+ putHexByte(uuid[3], p);
+ *p++ = '-';
+ putHexByte(uuid[4], p);
+ putHexByte(uuid[5], p);
+ *p++ = '-';
+ putHexByte(uuid[6], p);
+ putHexByte(uuid[7], p);
+ *p++ = '-';
+ putHexByte(uuid[8], p);
+ putHexByte(uuid[9], p);
+ *p++ = '-';
+ putHexByte(uuid[10], p);
+ putHexByte(uuid[11], p);
+ putHexByte(uuid[12], p);
+ putHexByte(uuid[13], p);
+ putHexByte(uuid[14], p);
+ putHexByte(uuid[15], p);
+ *p = '\0';
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static const dyld3::launch_cache::BinaryClosureData* callClosureDaemon(const char* mainExecPath, const char* envp[])
+{
+ // temp, until we can get a bootstrap_lookup that works from dyld
+#if 1
+ // Create a pipe
+ int sockets[2];
+ if ( ::pipe(sockets) < 0 ) {
+ dyld::log("error opening stream socket pair to closured\n");
+ return NULL;
+ }
+ //dyld::log("created sockets %d and %d\n", sockets[0], sockets[1]);
+ // use fork/exec to launch closured
+ int child = ::__fork();
+ if ( child == -1 ) {
+ dyld::log("error forking, errno=%d\n", errno);
+ return NULL;
+ }
+ if ( child ) {
+ // parent side
+ //dyld::log("parent side pid=%d\n", getpid());
+ ::close(sockets[1]);
+ SocketBasedClousureHeader header;
+ long amount = ::read(sockets[0], &header, sizeof(SocketBasedClousureHeader));
+ if ( amount != sizeof(SocketBasedClousureHeader) ) {
+ dyld::log("error reading, errno=%d\n", errno);
+ return NULL;
+ }
+ vm_address_t bufferAddress = 0;
+ if ( ::vm_allocate(mach_task_self(), &bufferAddress, header.length, VM_FLAGS_ANYWHERE) != 0 ) {
+ dyld::log("error allocating buffer\n");
+ return NULL;
+ }
+ amount = ::read(sockets[0], (void*)bufferAddress, header.length);
+ close(sockets[0]);
+ if ( amount != header.length ) {
+ dyld::log("dyld: error reading buffer header from closured, amount=%ld, errno=%d\n", amount, errno);
+ return NULL;
+ }
+ if ( header.success ) {
+ // make buffer read-only
+ vm_protect(mach_task_self(), bufferAddress, header.length, VM_PROT_READ, VM_PROT_READ);
+ return (const dyld3::launch_cache::BinaryClosureData*)bufferAddress;
+ }
+ else {
+ // buffer contains error message as to why closure could not be built
+ dyld::log("%s", (char*)bufferAddress);
+ ::vm_deallocate(mach_task_self(), bufferAddress, header.length);
+ return NULL;
+ }
+ }
+ else {
+ // child side
+ //dyld::log("child side pid=%d\n", getpid());
+ close(sockets[0]);
+ const char* closuredPath = "/usr/libexec/closured";
+ char pipeStr[8];
+ pipeStr[0] = '0' + sockets[1];
+ pipeStr[1] = '\0';
+ const char* argv[32];
+ char cacheUuidString[64];
+ char cacheAddrString[64];
+ char cacheSizeString[64];
+ int i = 0;
+ uuid_t cacheUUID;
+ sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+ makeHexLong((long)sSharedCacheLoadInfo.loadAddress, cacheAddrString);
+ makeHexLong((long)sSharedCacheLoadInfo.loadAddress->mappedSize(), cacheSizeString);
+ makeUUID(cacheUUID, cacheUuidString);
+ argv[i++] = closuredPath;
+ argv[i++] = "-create_closure";
+ argv[i++] = mainExecPath;
+ argv[i++] = "-pipefd";
+ argv[i++] = pipeStr;
+ argv[i++] = "-cache_uuid";
+ argv[i++] = cacheUuidString;
+ argv[i++] = "-cache_address";
+ argv[i++] = cacheAddrString;
+ argv[i++] = "-cache_size";
+ argv[i++] = cacheSizeString;
+ for (const char**p=envp; *p != NULL; ++p) {
+ const char* envToCheck = *p;
+ for (const char* dyldEnvVar : sEnvVarsToCheck) {
+ size_t dyldEnvVarLen = strlen(dyldEnvVar);
+ if ( (strncmp(dyldEnvVar, envToCheck, dyldEnvVarLen) == 0) && (envToCheck[dyldEnvVarLen] == '=') ) {
+ argv[i++] = "-env";
+ argv[i++] = envToCheck;
+ }
+ }
+ }
+ argv[i] = nullptr;
+ //dyld::log("closured args:\n");
+ //for (int j=0; argv[j] != nullptr; ++j)
+ // dyld::log(" argv[%d]=%s\n", j, argv[j]);
+ execve(closuredPath, (char**)argv, nullptr);
+ dyld::log("exec() of closured failed, errno=%d\n", errno);
+ }
+ return NULL;
+
+#else
+ // get port to closured
+ mach_port_t serverPort = dyld3::loader::lookupClosuredPort();
+ if ( serverPort == MACH_PORT_NULL )
+ return NULL;
+
+ // build env var list
+ char envBuffer[2048];
+ char* s = envBuffer;
+ for (const char* envVar : sEnvVarsToCheck) {
+ if ( const char* valueFromEnv = _simple_getenv(envp, envVar) ) {
+ strcpy(s, envVar);
+ strcat(s, "=");
+ strcat(s, valueFromEnv);
+ s += strlen(s)+1;
+ }
+ }
+ *s++ = '\0';
+
+ // get uuid of main executable
+ dyld3::MachOParser mainParser((mach_header*)sMainExecutableMachHeader);
+ uuid_t mainUuid;
+ mainParser.getUuid(mainUuid);
+
+ // message closured to build closure
+ bool success = false;
+ vm_offset_t reply = 0;
+ uint32_t replySize = 0;
+ if ( closured_CreateLaunchClosure(serverPort, sExecPath, sSharedCachePath, mainUuid, envBuffer, &success, &reply, &replySize) != KERN_SUCCESS )
+ return NULL;
+
+ // release server port
+ mach_port_deallocate(mach_task_self(), serverPort);
+
+ if ( success )
+ return (const dyld3::launch_cache::BinaryClosureData*)reply;
+
+ dyld::log("closure failed to build: %s\n", (char*)reply);
+ return NULL;
+#endif
+}
+#endif // !TARGET_IPHONE_SIMULATOR
+
+
+#if !__MAC_OS_X_VERSION_MIN_REQUIRED
+static const char* sWhiteListDirs[] = {
+ "/bin/",
+ "/sbin/",
+ "/usr/bin/"
+};
+#endif
+
+static bool inWhiteList(const char* execPath)
+{
+ // First test to see if we forced in dyld2 via a kernel boot-arg
+ if ( dyld3::loader::bootArgsContains("force_dyld2=1") )
+ return false;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+
+ // rdar://problem/32701418: Don't use dyld3 for i386 for now.
+#if __i386__
+ return false;
+#else
+
+
+ return true;
+#endif // #if __i386__
+
+#else
+ // <rdar://problem/33171968> enable dyld3 mode for all OS programs when using customer dyld cache (no roots)
+ if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeProduction) )
+ return true;
+
+ for (const char* dir : sWhiteListDirs) {
+ if ( strncmp(dir, sExecPath, strlen(dir)) == 0 ) {
+ return true;
+ }
+ }
+ return dyld3::loader::bootArgsContains("force_dyld3=1");
+#endif
+}
//
// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
+ dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_DYLD, 0, 0);
+
+ // Grab the cdHash of the main executable from the environment
+ uint8_t mainExecutableCDHashBuffer[20];
+ const uint8_t* mainExecutableCDHash = nullptr;
+ if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) )
+ mainExecutableCDHash = mainExecutableCDHashBuffer;
+
+ // Trace dyld's load
+ notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file"));
+#if !TARGET_IPHONE_SIMULATOR
+ // Trace the main executable's load
+ notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
+#endif
+
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
+ sMainExecutableSlide = mainExecutableSlide;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// 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];
sExecPath = s;
}
}
+
// Remember short name of process for later logging
sExecShortName = ::strrchr(sExecPath, '/');
if ( sExecShortName != NULL )
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
getHostInfo(mainExecutableMH, mainExecutableSlide);
+
+ // load shared cache
+ checkSharedRegionDisable((mach_header*)mainExecutableMH);
+#if TARGET_IPHONE_SIMULATOR
+ // <HACK> until <rdar://30773711> is fixed
+ gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
+ // </HACK>
+#endif
+ if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
+ mapSharedCache();
+ }
+
+ if ( (sEnableClosures || inWhiteList(sExecPath)) && (sSharedCacheLoadInfo.loadAddress != nullptr) ) {
+ if ( sSharedCacheLoadInfo.loadAddress->header.formatVersion == dyld3::launch_cache::binary_format::kFormatVersion ) {
+ const dyld3::launch_cache::BinaryClosureData* mainClosureData;
+ // check for closure in cache first
+ dyld3::DyldCacheParser cacheParser(sSharedCacheLoadInfo.loadAddress, false);
+ mainClosureData = cacheParser.findClosure(sExecPath);
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( mainClosureData == nullptr ) {
+ // see if this is an OS app that was moved
+ if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) {
+ dyld3::MachOParser mainParser((mach_header*)mainExecutableMH);
+ uint32_t textOffset;
+ uint32_t textSize;
+ if ( !mainParser.isFairPlayEncrypted(textOffset, textSize) ) {
+ __block bool hasEmbeddedDylibs = false;
+ mainParser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool& stop) {
+ if ( loadPath[0] == '@' ) {
+ hasEmbeddedDylibs = true;
+ stop = true;
+ }
+ });
+ if ( !hasEmbeddedDylibs ) {
+ char altPath[1024];
+ const char* lastSlash = strrchr(sExecPath, '/');
+ if ( lastSlash != nullptr ) {
+ strlcpy(altPath, "/private/var/staged_system_apps", sizeof(altPath));
+ strlcat(altPath, lastSlash, sizeof(altPath));
+ strlcat(altPath, ".app", sizeof(altPath));
+ strlcat(altPath, lastSlash, sizeof(altPath));
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("try path: %s\n", altPath);
+ mainClosureData = cacheParser.findClosure(altPath);
+ }
+ }
+ }
+ }
+ }
+ #endif
+ if ( gLinkContext.verboseWarnings && (mainClosureData != nullptr) )
+ dyld::log("dyld: found closure %p in dyld shared cache\n", mainClosureData);
+ #if !TARGET_IPHONE_SIMULATOR
+ if ( (mainClosureData == nullptr) || !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, true, envp) ) {
+ mainClosureData = nullptr;
+ if ( sEnableClosures ) {
+ // if forcing closures, and no closure in cache, or it is invalid, then RPC to closured
+ mainClosureData = callClosureDaemon(sExecPath, envp);
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closured return %p for %s\n", mainClosureData, sExecPath);
+ if ( (mainClosureData != nullptr) && !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, false, envp) ) {
+ // some how freshly generated closure is invalid...
+ mainClosureData = nullptr;
+ }
+ }
+ }
+ #endif
+ // try using launch closure
+ if ( mainClosureData != nullptr ) {
+ CRSetCrashLogMessage("dyld3: launch started");
+ if ( launchWithClosure(mainClosureData, sSharedCacheLoadInfo.loadAddress, (mach_header*)mainExecutableMH, mainExecutableSlide,
+ argc, argv, envp, apple, &result, startGlue) ) {
+ if (sSkipMain)
+ result = (uintptr_t)&fake_main;
+ return result;
+ }
+ else {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: unable to use closure %p\n", mainClosureData);
+ }
+ }
+ }
+ else {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: not using closure because shared cache format version does not match dyld's\n");
+ }
+ // could not use closure info, launch old way
+ }
+
+
// install gdb notifier
stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
try {
// add dyld itself to UUID list
addDyldImageToUUIDList();
- notifyKernelAboutDyld();
#if SUPPORT_ACCELERATE_TABLES
bool mainExcutableAlreadyRebased = false;
+ if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
+ struct stat statBuf;
+ if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
+ sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
+ }
reloadAllImages:
#endif
gLinkContext.strictMachORequired = true;
#endif
- // load shared cache
- checkSharedRegionDisable();
- #if DYLD_SHARED_CACHE_SUPPORT
- 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
// <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;
+ if ( (sSharedCacheLoadInfo.loadAddress != NULL) && (sSharedCacheLoadInfo.loadAddress->header.mappingOffset >= 0x78) && (sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset != 0) ) {
+ uint32_t count = sSharedCacheLoadInfo.loadAddress->header.branchPoolsCount;
dyld_image_info info[count];
- const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset);
+ const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCacheLoadInfo.loadAddress + sSharedCacheLoadInfo.loadAddress->header.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;
+ uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheLoadInfo.slide;
info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
info[poolIndex].imageFileModDate = 0;
gProcessInfo->notification(dyld_image_adding, count, info);
}
}
- #endif
CRSetCrashLogMessage("dyld: launch, running initializers");
#if SUPPORT_OLD_CRT_INITIALIZATION
#endif
// notify any montoring proccesses that this process is about to enter main()
+ dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN_DYLD2, 0, 0);
notifyMonitoringDyldMain();
// find entry point for main executable
}
CRSetCrashLogMessage(NULL);
+
+ if (sSkipMain) {
+ dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+ result = (uintptr_t)&fake_main;
+ *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
+ }
return result;
}
-
} // namespace
# 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
#if SUPPORT_ACCELERATE_TABLES
extern bool gLogAppAPIs;
#endif
-#if DYLD_SHARED_CACHE_SUPPORT
extern bool gSharedCacheOverridden;
-#endif
extern const struct LibSystemHelpers* gLibSystemHelpers;
#if SUPPORT_OLD_CRT_INITIALIZATION
extern bool gRunInitializersOldWay;
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 uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset);
-#if DYLD_SHARED_CACHE_SUPPORT
extern bool inSharedCache(const char* path);
-#endif
#if LOG_BINDINGS
extern void logBindings(const char* format, ...);
#endif
#include "ImageLoaderMachO.h"
#include "dyld.h"
#include "dyldLibSystemInterface.h"
+#include "DyldSharedCache.h"
#undef _POSIX_C_SOURCE
#include "dlfcn.h"
+
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+ const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+}
+
// from dyldExceptions.c
extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key);
extern const mach_header* allImagesIndexedMachHeader(uint32_t index);
extern const char* allImagesIndexedPath(uint32_t index);
+extern "C" int _dyld_func_lookup(const char* name, void** address);
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
{"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden },
{"__dyld_process_is_restricted", (void*)dyld::processIsRestricted },
{"__dyld_dynamic_interpose", (void*)dyld_dynamic_interpose },
-#if DYLD_SHARED_CACHE_SUPPORT
{"__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_install_handlers", (void*)_dyld_install_handlers },
{"__dyld_link_edit_error", (void*)NSLinkEditError },
{"__dyld_unlink_module", (void*)NSUnLinkModule },
- {"__dyld_bind_objc_module", (void*)_dyld_bind_objc_module },
{"__dyld_bind_fully_image_containing_address", (void*)_dyld_bind_fully_image_containing_address },
{"__dyld_image_containing_address", (void*)_dyld_image_containing_address },
{"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler },
{"__dyld_NSCreateObjectFileImageFromMemory", (void*)NSCreateObjectFileImageFromMemory },
{"__dyld_NSDestroyObjectFileImage", (void*)NSDestroyObjectFileImage },
{"__dyld_NSLinkModule", (void*)NSLinkModule },
- {"__dyld_NSHasModInitObjectFileImage", (void*)NSHasModInitObjectFileImage },
{"__dyld_NSSymbolDefinitionCountInObjectFileImage", (void*)NSSymbolDefinitionCountInObjectFileImage },
{"__dyld_NSSymbolDefinitionNameInObjectFileImage", (void*)NSSymbolDefinitionNameInObjectFileImage },
{"__dyld_NSIsSymbolDefinedInObjectFileImage", (void*)NSIsSymbolDefinedInObjectFileImage },
return FALSE;
}
-void _dyld_bind_objc_module(const void *objc_module)
-{
- if ( dyld::gLogAPIs )
- dyld::log("%s(%p)\n", __func__, objc_module);
- // do nothing, with new dyld everything already bound
-}
-
-
bool _dyld_bind_fully_image_containing_address(const void* address)
{
if ( dyld::gLogAPIs )
static bool validOFI(NSObjectFileImage objectFileImage)
{
- const int ofiCount = sObjectFileImages.size();
- for (int i=0; i < ofiCount; ++i) {
+ const size_t ofiCount = sObjectFileImages.size();
+ for (size_t i=0; i < ofiCount; ++i) {
if ( sObjectFileImages[i] == objectFileImage )
return true;
}
return false;
}
-bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage)
-{
- if ( dyld::gLogAPIs )
- dyld::log("%s(%p)\n", __func__, objectFileImage);
- return objectFileImage->image->needsInitialization();
-}
-
uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)
{
if ( dyld::gLogAPIs )
// Only delete image if there is no ofi referencing it
// That means the ofi was destroyed after linking, so no one is left to delete this image
- const int ofiCount = sObjectFileImages.size();
+ const size_t ofiCount = sObjectFileImages.size();
bool found = false;
- for (int i=0; i < ofiCount; ++i) {
+ for (size_t i=0; i < ofiCount; ++i) {
NSObjectFileImage ofi = sObjectFileImages[i];
if ( ofi->image == image )
found = true;
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
if ( dyld::inSharedCache(path) )
return true;
-#endif
bool result = false;
std::vector<const char*> rpathsFromCallerImage;
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;
+ // found in cache, but under a different path
+ const char* betterPath = dyld::getPathFromIndex(cacheIndex);
+ if ( (betterPath != NULL) && dyld::dlopenFromCache(betterPath, mode, &result) ) {
+ // Note: dlopenFromCache() releases the lock
+ if ( dyld::gLogAPIs )
+ dyld::log(" %s(%s) ==> %p\n", __func__, path, result);
+ return result;
}
}
#endif
bool dyld_shared_cache_some_image_overridden()
{
- #if DYLD_SHARED_CACHE_SUPPORT
return dyld::gSharedCacheOverridden;
- #else
- return true;
- #endif
}
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;
+ const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader();
+ if ( cache != nullptr ) {
+ const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+ uintptr_t roStart = (uintptr_t)cache;
+ uintptr_t roEnd = roStart + (uintptr_t)mappings[0].size;
if ( (roStart < checkStart) && (checkEnd < roEnd) )
return true;
- }
-#endif
+ }
// Otherwise find if addr is in a dyld loaded image
ImageLoader* image = dyld::findImageContainingAddress(addr);
const void* _dyld_get_shared_cache_range(size_t* length)
{
-#if DYLD_SHARED_CACHE_SUPPORT
- uintptr_t cacheEndAddr = (dyld_shared_cache_ranges.ranges[2].start + dyld_shared_cache_ranges.ranges[2].length);
- *length = cacheEndAddr - dyld_shared_cache_ranges.ranges[0].start;
- return (void*)(dyld_shared_cache_ranges.ranges[0].start);
-#else
- return NULL;
-#endif
+ const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader();
+ if ( cache != nullptr ) {
+ const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+ *length = (size_t)((mappings[2].address + mappings[2].size) - mappings[0].address);
+ return cache;
+ }
+ return nullptr;
}
#include "dyldLock.h"
#include "start_glue.h"
+#include "../dyld3/APIs.h"
+#include "../dyld3/AllImages.h"
+
+
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+ const struct dyld_all_image_infos* _dyld_get_all_image_infos() __attribute__((visibility("hidden")));
+}
+
+
extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
extern "C" void __cxa_finalize(const void *dso);
extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int count);
+//
+// private interface between libSystem.dylib and dyld
+//
+extern "C" int _dyld_func_lookup(const char* dyld_func_name, void **address);
+
+
+extern bool gUseDyld3;
#ifndef LC_VERSION_MIN_MACOSX
#define LC_VERSION_MIN_MACOSX 0x24
#define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
#endif
+#ifndef LC_BUILD_VERSION
+ #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+ /*
+ * The build_version_command contains the min OS version on which this
+ * binary was built to run for its platform. The list of known platforms and
+ * tool values following it.
+ */
+ struct build_version_command {
+ uint32_t cmd; /* LC_BUILD_VERSION */
+ uint32_t cmdsize; /* sizeof(struct build_version_command) plus */
+ /* ntools * sizeof(struct build_tool_version) */
+ uint32_t platform; /* platform */
+ uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t ntools; /* number of tool entries following this */
+ };
+
+ struct build_tool_version {
+ uint32_t tool; /* enum for the tool */
+ uint32_t version; /* version number of the tool */
+ };
+
+ /* Known values for the platform field above. */
+ #define PLATFORM_MACOS 1
+ #define PLATFORM_IOS 2
+ #define PLATFORM_TVOS 3
+ #define PLATFORM_WATCHOS 4
+ #define PLATFORM_BRIDGEOS 5
+
+ /* Known values for the tool field above. */
+ #define TOOL_CLANG 1
+ #define TOOL_SWIFT 2
+ #define TOOL_LD 3
+#endif
+
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
void NSInstallLinkEditErrorHandlers(
const NSLinkEditErrorHandlers* handlers)
{
+ if ( gUseDyld3 )
+ return dyld3::NSInstallLinkEditErrorHandlers(handlers);
+
DYLD_LOCK_THIS_BLOCK;
typedef void (*ucallback_t)(const char* symbol_name);
typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler);
NSNameOfModule(
NSModule module)
{
+ if ( gUseDyld3 )
+ return dyld3::NSNameOfModule(module);
+
DYLD_LOCK_THIS_BLOCK;
static const char* (*p)(NSModule module) = NULL;
NSLibraryNameForModule(
NSModule module)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLibraryNameForModule(module);
+
DYLD_LOCK_THIS_BLOCK;
static const char* (*p)(NSModule module) = NULL;
NSIsSymbolNameDefined(
const char* symbolName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSIsSymbolNameDefined(symbolName);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const char* symbolName) = NULL;
const char* symbolName,
const char* libraryNameHint)
{
+ if ( gUseDyld3 )
+ return dyld3::NSIsSymbolNameDefinedWithHint(symbolName, libraryNameHint);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const char* symbolName,
const char* libraryNameHint) = NULL;
const struct mach_header *image,
const char* symbolName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSIsSymbolNameDefinedInImage(image, symbolName);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const struct mach_header *image,
const char* symbolName) = NULL;
NSLookupAndBindSymbol(
const char* symbolName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLookupAndBindSymbol(symbolName);
+
DYLD_LOCK_THIS_BLOCK;
static NSSymbol (*p)(const char* symbolName) = NULL;
const char* symbolName,
const char* libraryNameHint)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLookupAndBindSymbolWithHint(symbolName, libraryNameHint);
+
DYLD_LOCK_THIS_BLOCK;
static NSSymbol (*p)(const char* symbolName,
const char* libraryNameHint) = NULL;
NSModule module,
const char* symbolName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLookupSymbolInModule(module, symbolName);
+
DYLD_LOCK_THIS_BLOCK;
static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL;
const char* symbolName,
uint32_t options)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLookupSymbolInImage(image, symbolName, options);
+
DYLD_LOCK_THIS_BLOCK;
- static NSSymbol (*p)(const struct mach_header *image,
+ static NSSymbol (*p)(const struct mach_header *image,
const char* symbolName,
uint32_t options) = NULL;
NSNameOfSymbol(
NSSymbol symbol)
{
+ if ( gUseDyld3 )
+ return dyld3::NSNameOfSymbol(symbol);
+
DYLD_LOCK_THIS_BLOCK;
static char * (*p)(NSSymbol symbol) = NULL;
NSAddressOfSymbol(
NSSymbol symbol)
{
+ if ( gUseDyld3 )
+ return dyld3::NSAddressOfSymbol(symbol);
+
DYLD_LOCK_THIS_BLOCK;
static void * (*p)(NSSymbol symbol) = NULL;
NSModuleForSymbol(
NSSymbol symbol)
{
+ if ( gUseDyld3 )
+ return dyld3::NSModuleForSymbol(symbol);
+
DYLD_LOCK_THIS_BLOCK;
static NSModule (*p)(NSSymbol symbol) = NULL;
NSAddLibrary(
const char* pathName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSAddLibrary(pathName);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const char* pathName) = NULL;
NSAddLibraryWithSearching(
const char* pathName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSAddLibrary(pathName);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const char* pathName) = NULL;
const char* image_name,
uint32_t options)
{
+ if ( gUseDyld3 )
+ return dyld3::NSAddImage(image_name, options);
+
DYLD_LOCK_THIS_BLOCK;
static const struct mach_header * (*p)(const char* image_name,
uint32_t options) = NULL;
*/
int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSVersionOfLinkTimeLibrary(libraryName);
+
// Lazily call _NSGetMachExecuteHeader() and cache result
#if __LP64__
static mach_header_64* mh = NULL;
*/
int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSVersionOfRunTimeLibrary(libraryName);
+
uint32_t n = _dyld_image_count();
for(uint32_t i = 0; i < n; i++){
const mach_header* mh = _dyld_get_image_header(i);
#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
-static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadCommand, uint32_t* minOS, uint32_t* sdk)
+static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* platform, uint32_t* minOS, uint32_t* sdk)
{
const load_command* startCmds = NULL;
if ( mh->magic == MH_MAGIC_64 )
return 0;
}
const version_min_command* versCmd;
+ const build_version_command* buildVersCmd;
switch ( cmd->cmd ) {
case LC_VERSION_MIN_IPHONEOS:
+ versCmd = (version_min_command*)cmd;
+ *platform = PLATFORM_IOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ return true;
case LC_VERSION_MIN_MACOSX:
+ versCmd = (version_min_command*)cmd;
+ *platform = PLATFORM_MACOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ return true;
case LC_VERSION_MIN_TVOS:
+ versCmd = (version_min_command*)cmd;
+ *platform = PLATFORM_TVOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ return true;
case LC_VERSION_MIN_WATCHOS:
versCmd = (version_min_command*)cmd;
- *loadCommand = versCmd->cmd;
- *minOS = versCmd->version;
- *sdk = versCmd->sdk;
+ *platform = PLATFORM_WATCHOS;
+ *minOS = versCmd->version;
+ *sdk = versCmd->sdk;
+ return true;
+ case LC_BUILD_VERSION:
+ buildVersCmd = (build_version_command*)cmd;
+ *platform = buildVersCmd->platform;
+ *minOS = buildVersCmd->minos;
+ *sdk = buildVersCmd->sdk;
return true;
}
cmd = nextCmd;
uint32_t dyld_get_program_sdk_watch_os_version()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_program_sdk_watch_os_version();
+
const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
- uint32_t loadCommand;
+ uint32_t platform;
uint32_t minOS;
uint32_t sdk;
- if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
- if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ if ( platform == PLATFORM_WATCHOS )
return sdk;
}
return 0;
}
uint32_t dyld_get_program_min_watch_os_version()
+{
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_program_min_watch_os_version();
+
+ const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+ uint32_t platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ if ( platform == PLATFORM_WATCHOS )
+ return minOS; // return raw minOS (not mapped to iOS version)
+ }
+ return 0;
+}
+
+#endif
+
+
+
+#if TARGET_OS_BRIDGE
+static uint32_t bridgeVersToIOSVers(uint32_t vers)
+{
+ return vers + 0x00090000;
+}
+
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+ const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+ uint32_t platform;
+ uint32_t minOS;
+ uint32_t sdk;
+
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ if ( platform == PLATFORM_BRIDGEOS )
+ return sdk;
+ }
+ return 0;
+}
+
+uint32_t dyld_get_program_min_bridge_os_version()
{
const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
- uint32_t loadCommand;
+ uint32_t platform;
uint32_t minOS;
uint32_t sdk;
- if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
- if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ if ( platform == PLATFORM_BRIDGEOS )
return minOS; // return raw minOS (not mapped to iOS version)
}
return 0;
*/
uint32_t dyld_get_sdk_version(const mach_header* mh)
{
- uint32_t loadCommand;
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_sdk_version(mh);
+
+ uint32_t platform;
uint32_t minOS;
uint32_t sdk;
- if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
- switch (loadCommand) {
-#if __WATCH_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_WATCHOS:
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ switch (platform) {
+#if TARGET_OS_BRIDGE
+ case PLATFORM_BRIDGEOS:
+ // new binary. sdk version looks like "2.0" but API wants "11.0"
+ return bridgeVersToIOSVers(sdk);
+ case PLATFORM_IOS:
+ // old binary. sdk matches API semantics so can return directly.
+ return sdk;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+ case PLATFORM_WATCHOS:
// new binary. sdk version looks like "2.0" but API wants "9.0"
return watchVersToIOSVers(sdk);
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_IOS:
// old binary. sdk matches API semantics so can return directly.
return sdk;
#elif __TV_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_TVOS:
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_TVOS:
+ case PLATFORM_IOS:
return sdk;
#elif __IPHONE_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_IOS:
if ( sdk != 0 ) // old binaries might not have SDK set
return sdk;
break;
#else
- case LC_VERSION_MIN_MACOSX:
+ case PLATFORM_MACOS:
if ( sdk != 0 ) // old binaries might not have SDK set
return sdk;
break;
}
}
-#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED
+#if __WATCH_OS_VERSION_MIN_REQUIRED || __TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
// All WatchOS and tv OS binaries should have version load command.
return 0;
#else
uint32_t dyld_get_program_sdk_version()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_program_sdk_version();
+
return dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader());
}
uint32_t dyld_get_min_os_version(const struct mach_header* mh)
{
- uint32_t loadCommand;
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_min_os_version(mh);
+
+ uint32_t platform;
uint32_t minOS;
uint32_t sdk;
- if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
- switch (loadCommand) {
-#if __WATCH_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_WATCHOS:
+ if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+ switch (platform) {
+#if TARGET_OS_BRIDGE
+ case PLATFORM_BRIDGEOS:
+ // new binary. sdk version looks like "2.0" but API wants "11.0"
+ return bridgeVersToIOSVers(minOS);
+ case PLATFORM_IOS:
+ // old binary. sdk matches API semantics so can return directly.
+ return minOS;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+ case PLATFORM_WATCHOS:
// new binary. OS version looks like "2.0" but API wants "9.0"
return watchVersToIOSVers(minOS);
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_IOS:
// old binary. OS matches API semantics so can return directly.
return minOS;
#elif __TV_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_TVOS:
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_TVOS:
+ case PLATFORM_IOS:
return minOS;
#elif __IPHONE_OS_VERSION_MIN_REQUIRED
- case LC_VERSION_MIN_IPHONEOS:
+ case PLATFORM_IOS:
return minOS;
#else
- case LC_VERSION_MIN_MACOSX:
+ case PLATFORM_MACOS:
return minOS;
#endif
}
uint32_t dyld_get_program_min_os_version()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_get_program_min_os_version();
+
return dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader());
}
bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_uuid(mh, uuid);
+
const load_command* startCmds = NULL;
if ( mh->magic == MH_MAGIC_64 )
startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
const char* pathName,
NSObjectFileImage *objectFileImage)
{
+ if ( gUseDyld3 )
+ return dyld3::NSCreateObjectFileImageFromFile(pathName, objectFileImage);
+
DYLD_LOCK_THIS_BLOCK;
static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL;
size_t size,
NSObjectFileImage *objectFileImage)
{
+ if ( gUseDyld3 )
+ return dyld3::NSCreateObjectFileImageFromMemory(address, size, objectFileImage);
+
DYLD_LOCK_THIS_BLOCK;
static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL;
NSDestroyObjectFileImage(
NSObjectFileImage objectFileImage)
{
+ if ( gUseDyld3 )
+ return dyld3::NSDestroyObjectFileImage(objectFileImage);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(NSObjectFileImage) = NULL;
const char* moduleName,
uint32_t options)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLinkModule(objectFileImage, moduleName, options);
+
DYLD_LOCK_THIS_BLOCK;
static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL;
NSSymbolDefinitionCountInObjectFileImage(
NSObjectFileImage objectFileImage)
{
+ if ( gUseDyld3 )
+ return dyld3::NSSymbolDefinitionCountInObjectFileImage(objectFileImage);
+
DYLD_LOCK_THIS_BLOCK;
static uint32_t (*p)(NSObjectFileImage) = NULL;
NSObjectFileImage objectFileImage,
uint32_t ordinal)
{
+ if ( gUseDyld3 )
+ return dyld3::NSSymbolDefinitionNameInObjectFileImage(objectFileImage, ordinal);
+
DYLD_LOCK_THIS_BLOCK;
static const char* (*p)(NSObjectFileImage, uint32_t) = NULL;
NSSymbolReferenceCountInObjectFileImage(
NSObjectFileImage objectFileImage)
{
+ if ( gUseDyld3 )
+ return dyld3::NSSymbolReferenceCountInObjectFileImage(objectFileImage);
+
DYLD_LOCK_THIS_BLOCK;
static uint32_t (*p)(NSObjectFileImage) = NULL;
uint32_t ordinal,
bool *tentative_definition) /* can be NULL */
{
+ if ( gUseDyld3 )
+ return dyld3::NSSymbolReferenceNameInObjectFileImage(objectFileImage, ordinal, tentative_definition);
+
DYLD_LOCK_THIS_BLOCK;
static const char* (*p)(NSObjectFileImage, uint32_t, bool*) = NULL;
NSObjectFileImage objectFileImage,
const char* symbolName)
{
+ if ( gUseDyld3 )
+ return dyld3::NSIsSymbolDefinedInObjectFileImage(objectFileImage, symbolName);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(NSObjectFileImage, const char*) = NULL;
const char* sectionName,
unsigned long *size) /* can be NULL */
{
+ if ( gUseDyld3 )
+ return dyld3::NSGetSectionDataInObjectFileImage(objectFileImage, segmentName, sectionName, size);
+
DYLD_LOCK_THIS_BLOCK;
static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL;
const char* *fileName,
const char* *errorString)
{
+ if ( gUseDyld3 )
+ return dyld3::NSLinkEditError(c, errorNumber, fileName, errorString);
+
DYLD_LOCK_THIS_BLOCK;
static void (*p)(NSLinkEditErrors *c,
int *errorNumber,
NSModule module,
uint32_t options)
{
+ if ( gUseDyld3 )
+ return dyld3::NSUnLinkModule(module, options);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(NSModule module, uint32_t options) = NULL;
char *buf,
uint32_t *bufsize)
{
+ if ( gUseDyld3 )
+ return dyld3::_NSGetExecutablePath(buf, bufsize);
+
DYLD_NO_LOCK_THIS_BLOCK;
static int (*p)(char *buf, uint32_t *bufsize) = NULL;
_dyld_register_func_for_add_image(
void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_register_func_for_add_image(func);
+
DYLD_LOCK_THIS_BLOCK;
typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
static void (*p)(callback_t func) = NULL;
_dyld_register_func_for_remove_image(
void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_register_func_for_remove_image(func);
+
DYLD_LOCK_THIS_BLOCK;
typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
static void (*p)(callback_t func) = NULL;
p(module, objc_module, size);
}
-/*
- * _dyld_bind_objc_module() is passed a pointer to something in an (__OBJC,
- * __module) section and causes the module that is associated with that address
- * to be bound.
- */
-void
-_dyld_bind_objc_module(const void* objc_module)
-{
- DYLD_LOCK_THIS_BLOCK;
- static void (*p)(const void *objc_module) = NULL;
-
- if(p == NULL)
- _dyld_func_lookup("__dyld_bind_objc_module", (void**)&p);
- p(objc_module);
-}
#endif
#if DEPRECATED_APIS_SUPPORTED
uint32_t
_dyld_image_count(void)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_image_count();
+
DYLD_NO_LOCK_THIS_BLOCK;
static uint32_t (*p)(void) = NULL;
const struct mach_header *
_dyld_get_image_header(uint32_t image_index)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_header(image_index);
+
DYLD_NO_LOCK_THIS_BLOCK;
static struct mach_header * (*p)(uint32_t image_index) = NULL;
intptr_t
_dyld_get_image_vmaddr_slide(uint32_t image_index)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_vmaddr_slide(image_index);
+
DYLD_NO_LOCK_THIS_BLOCK;
static unsigned long (*p)(uint32_t image_index) = NULL;
const char*
_dyld_get_image_name(uint32_t image_index)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_name(image_index);
+
DYLD_NO_LOCK_THIS_BLOCK;
static const char* (*p)(uint32_t image_index) = NULL;
// SPI in Mac OS X 10.6
intptr_t _dyld_get_image_slide(const struct mach_header* mh)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_slide(mh);
+
DYLD_NO_LOCK_THIS_BLOCK;
static intptr_t (*p)(const struct mach_header*) = NULL;
}
+#if DEPRECATED_APIS_SUPPORTED
bool
_dyld_image_containing_address(const void* address)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_image_containing_address(address);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const void*) = NULL;
_dyld_get_image_header_containing_address(
const void* address)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_image_header_containing_address(address);
+
DYLD_LOCK_THIS_BLOCK;
static const struct mach_header * (*p)(const void*) = NULL;
return p(address);
}
-
-#if DEPRECATED_APIS_SUPPORTED
bool _dyld_launched_prebound(void)
{
DYLD_LOCK_THIS_BLOCK;
return ( val != 0 );
}
-#if DYLD_SHARED_CACHE_SUPPORT
static void shared_cache_missing()
{
// leave until dyld's that might call this are rare
{
// leave until dyld's that might call this are rare
}
-#endif // DYLD_SHARED_CACHE_SUPPORT
// the table passed to dyld containing thread helpers
static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease,
&getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit,
- #if DYLD_SHARED_CACHE_SUPPORT
&shared_cache_missing, &shared_cache_out_of_date,
- #else
- NULL, NULL,
- #endif
NULL, NULL,
&pthread_key_create, &pthread_setspecific,
&malloc_size,
// and call dyld, registering the helper functions.
//
extern "C" void tlv_initializer();
-extern "C" void _dyld_initializer();
void _dyld_initializer()
{
void (*p)(dyld::LibSystemHelpers*);
- _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
- if(p != NULL)
- p(&sHelpers);
-
+ if ( gUseDyld3 ) {
+ dyld3::gAllImages.applyInitialImages();
+ }
+ else {
+ _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
+ if(p != NULL)
+ p(&sHelpers);
+ }
+
tlv_initializer();
}
char* dlerror()
{
+ if ( gUseDyld3 )
+ return dyld3::dlerror();
+
DYLD_LOCK_THIS_BLOCK;
static char* (*p)() = NULL;
int dladdr(const void* addr, Dl_info* info)
{
+ if ( gUseDyld3 )
+ return dyld3::dladdr(addr, info);
+
DYLD_LOCK_THIS_BLOCK;
static int (*p)(const void* , Dl_info*) = NULL;
int dlclose(void* handle)
{
+ if ( gUseDyld3 )
+ return dyld3::dlclose(handle);
+
DYLD_LOCK_THIS_BLOCK;
static int (*p)(void* handle) = NULL;
void* dlopen(const char* path, int mode)
{
+ if ( gUseDyld3 )
+ return dyld3::dlopen(path, mode);
+
// dlopen is special. locking is done inside dyld to allow initializer to run without lock
DYLD_NO_LOCK_THIS_BLOCK;
bool dlopen_preflight(const char* path)
{
+ if ( gUseDyld3 )
+ return dyld3::dlopen_preflight(path);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(const char* path) = NULL;
void* dlsym(void* handle, const char* symbol)
{
+ if ( gUseDyld3 )
+ return dyld3::dlsym(handle, symbol);
+
DYLD_LOCK_THIS_BLOCK;
static void* (*p)(void* handle, const char* symbol) = NULL;
return(p(handle, symbol));
}
-
const struct dyld_all_image_infos* _dyld_get_all_image_infos()
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_all_image_infos();
+
DYLD_NO_LOCK_THIS_BLOCK;
static struct dyld_all_image_infos* (*p)() = NULL;
#if SUPPORT_ZERO_COST_EXCEPTIONS
bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_find_unwind_sections(addr, info);
+
DYLD_NO_LOCK_THIS_BLOCK;
static void* (*p)(void*, dyld_unwind_sections*) = NULL;
const char* dyld_image_path_containing_address(const void* addr)
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_image_path_containing_address(addr);
+
DYLD_NO_LOCK_THIS_BLOCK;
static const char* (*p)(const void*) = NULL;
const struct mach_header* dyld_image_header_containing_address(const void* addr)
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_image_header_containing_address(addr);
+
DYLD_NO_LOCK_THIS_BLOCK;
static const mach_header* (*p)(const void*) = NULL;
bool dyld_shared_cache_some_image_overridden()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_shared_cache_some_image_overridden();
+
DYLD_NO_LOCK_THIS_BLOCK;
static bool (*p)() = NULL;
bool _dyld_get_shared_cache_uuid(uuid_t uuid)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_shared_cache_uuid(uuid);
+
DYLD_NO_LOCK_THIS_BLOCK;
static bool (*p)(uuid_t) = NULL;
const void* _dyld_get_shared_cache_range(size_t* length)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_get_shared_cache_range(length);
+
DYLD_NO_LOCK_THIS_BLOCK;
static const void* (*p)(size_t*) = NULL;
bool dyld_process_is_restricted()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_process_is_restricted();
+
DYLD_NO_LOCK_THIS_BLOCK;
static bool (*p)() = NULL;
return p();
}
-#if DYLD_SHARED_CACHE_SUPPORT
const char* dyld_shared_cache_file_path()
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_shared_cache_file_path();
+
DYLD_NO_LOCK_THIS_BLOCK;
static const char* (*p)() = NULL;
_dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p);
return p();
}
-#endif
void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count)
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_dynamic_interpose(mh, array, count);
+
DYLD_LOCK_THIS_BLOCK;
static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL;
// SPI called __fork
void _dyld_fork_child()
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_fork_child();
+
DYLD_NO_LOCK_THIS_BLOCK;
static void (*p)() = 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))
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
+
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) ) {
+ if ( (allInfo != NULL) && (allInfo->sharedCacheBaseAddress != 0) && (memcmp(allInfo->sharedCacheUUID, cacheUuid, 16) == 0) ) {
// requested cache is already mapped, just re-use it
- cacheHeader = (dyld_cache_header*)(SHARED_REGION_BASE + allInfo->sharedCacheSlide);
+ cacheHeader = (dyld_cache_header*)(allInfo->sharedCacheBaseAddress);
needToUnmap = false;
}
else {
}
// walk imageText table and call callback for each entry
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+ const uint64_t cacheUnslidBaseAddress = mappings[0].address;
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.version = 2;
dylibTextInfo.loadAddressUnslid = p->loadAddress;
dylibTextInfo.textSegmentSize = p->textSegmentSize;
dylibTextInfo.path = (char*)cacheHeader + p->pathOffset;
::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16);
+ dylibTextInfo.textSegmentOffset = p->loadAddress - cacheUnslidBaseAddress;
callback(&dylibTextInfo);
}
int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
{
+ if ( gUseDyld3 )
+ return dyld3::dyld_shared_cache_iterate_text(cacheUuid, callback);
+
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)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_is_memory_immutable(addr, length);
+
DYLD_NO_LOCK_THIS_BLOCK;
static bool (*p)(const void*, size_t) = NULL;
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_objc_notify_register(mapped, init, unmapped);
+
DYLD_LOCK_THIS_BLOCK;
static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL;
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);
- };
-
+ kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state);
+ kern_return_t (*task_info)(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt);
+ kern_return_t (*thread_info)(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt);
+ bool (*kdebug_is_enabled)(uint32_t code);
+ int (*kdebug_trace)(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
+ };
extern const struct SyscallHelpers* gSyscallHelpers;
#include "ImageLoader.h"
#include "dyld.h"
+extern "C" void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]);
+
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#define INITIAL_UUID_IMAGE_COUNT 4
#else
#include "dyld_images.h"
#include "dyld_priv.h"
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+ const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+}
+
+namespace {
+
+void withRemoteBuffer(task_t task, vm_address_t remote_address, size_t remote_size, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer)) {
+ kern_return_t r = KERN_SUCCESS;
+ mach_vm_address_t local_address = 0;
+ mach_vm_address_t local_size = remote_size;
+ while (1) {
+ vm_prot_t cur_protection, max_protection;
+ r = mach_vm_remap(mach_task_self(),
+ &local_address,
+ local_size,
+ 0, // mask
+ VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN,
+ task,
+ remote_address,
+ TRUE, // Copy semantics: changes to this memory by the target process will not be visible in this process
+ &cur_protection,
+ &max_protection,
+ VM_INHERIT_DEFAULT);
+ //Do this here to allow chaining of multiple embedded blocks with a single error out;
+ if (kr != NULL) {
+ *kr = r;
+ }
+ if (r == KERN_SUCCESS) {
+ // We got someting, call the block and then exit
+ block(reinterpret_cast<void *>(local_address));
+ vm_deallocate(mach_task_self(), local_address, local_size);
+ break;
+ }
+ if (!allow_truncation) {
+ break;
+ }
+ // We did not get something, but we are allowed to truncate and try again
+ uint64_t trunc_size = ((remote_address + local_size - 1) & PAGE_MASK) + 1;
+ if (local_size <= trunc_size) {
+ //Even if we truncate it will be in the same page, time to accept defeat
+ break;
+ } else {
+ local_size -= trunc_size;
+ }
+ }
+}
+
+template<typename T>
+void withRemoteObject(task_t task, vm_address_t remote_address, kern_return_t *kr, void (^block)(T t))
+{
+ withRemoteBuffer(task, remote_address, sizeof(T), false, kr, ^(void *buffer) {
+ block(*reinterpret_cast<T *>(buffer));
+ });
+}
+};
//
// Opaque object returned by _dyld_process_info_create()
{
// 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));
+ bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion
+ && !myInfo->processDetachedFromSharedRegion
+ && ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0)
+ && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide));
unsigned countOfPathsNeedingCopying = 0;
if ( sameCacheAsThisProcess ) {
for (int i=0; i < allImageInfo.infoArrayCount; ++i) {
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) ) {
return NULL;
}
- unsigned imageCount = 0; // main executable and dyld
- uint64_t mainExecutableAddress = 0;
- uint64_t dyldAddress = 0;
+ __block unsigned imageCount = 0; // main executable and dyld
+ __block uint64_t mainExecutableAddress = 0;
+ __block uint64_t dyldAddress = 0;
char dyldPathBuffer[PATH_MAX+1];
char mainExecutablePathBuffer[PATH_MAX+1];
+ __block char * dyldPath = &dyldPathBuffer[0];
+ __block char * mainExecutablePath = &mainExecutablePathBuffer[0];
mach_vm_size_t size;
for (mach_vm_address_t address = 0; ; address += size) {
vm_region_basic_info_data_64_t info;
(vm_region_info_t)&info, &infoCount, &objectName);
if ( result != KERN_SUCCESS )
break;
- if ( info.protection == (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+ if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) )
+ continue;
// read start of vm region to verify it is a mach header
- mach_vm_size_t readSize = sizeof(mach_header_64);
- mach_header_64 mhBuffer;
- if ( mach_vm_read_overwrite(task, address, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) != KERN_SUCCESS )
- continue;
- if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) )
- continue;
- // now know the region is the start of a mach-o file
- if ( mhBuffer.filetype == MH_EXECUTE ) {
- mainExecutableAddress = address;
- int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePathBuffer, PATH_MAX);
- if ( len != 0 )
- mainExecutablePathBuffer[len] = '\0';
- ++imageCount;
- }
- else if ( mhBuffer.filetype == MH_DYLINKER ) {
- dyldAddress = address;
- int len = proc_regionfilename(pid, dyldAddress, dyldPathBuffer, PATH_MAX);
- if ( len != 0 )
- dyldPathBuffer[len] = '\0';
- ++imageCount;
- }
+ withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){
+ if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) )
+ return;
+ // now know the region is the start of a mach-o file
+ if ( mhBuffer.filetype == MH_EXECUTE ) {
+ mainExecutableAddress = address;
+ int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePath, PATH_MAX);
+ if ( len != 0 ) {
+ mainExecutablePath[len] = '\0';
+ }
+ ++imageCount;
+ }
+ else if ( mhBuffer.filetype == MH_DYLINKER ) {
+ dyldAddress = address;
+ int len = proc_regionfilename(pid, dyldAddress, dyldPath, PATH_MAX);
+ if ( len != 0 ) {
+ dyldPath[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);
// fill in info for dyld
if ( dyldAddress != 0 ) {
- if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPathBuffer) ) {
+ if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPath) ) {
if ( kr != NULL )
*kr = r;
free(obj);
// fill in info for each image
if ( mainExecutableAddress != 0 ) {
- if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePathBuffer) ) {
+ if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) {
if ( kr != NULL )
*kr = r;
free(obj);
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);
+ __block const char* retval = NULL;
+ withRemoteBuffer(task, stringAddressInTask, PATH_MAX+8, true, kr, ^(void *buffer) {
+ retval = addString(static_cast<const char *>(buffer));
+ });
+ return retval;
}
-
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;
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;
+ __block kern_return_t kr = KERN_SUCCESS;
+ withRemoteObject(task, imageAddress, &kr, ^(mach_header_64 mhBuffer) {
+ size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds;
+ withRemoteBuffer(task, imageAddress, headerPagesSize, false, &kr, ^(void * buffer) {
+ addInfoFromLoadCommands((mach_header*)buffer, imageAddress, headerPagesSize);
+ });
+ });
+ if (kr != KERN_SUCCESS) {
+ return kr;
}
- addInfoFromLoadCommands((mach_header*)localCopyBuffer, imageAddress, localCopyBufferSize);
- vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
}
_curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
_curImage++;
kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
{
- kern_return_t kr;
+ __block kern_return_t kr = KERN_SUCCESS;
_curImage->loadAddress = dyldAddress;
_curImage->segmentStartIndex = _curSegmentIndex;
if ( localPath != NULL ) {
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;
+ withRemoteObject(task, dyldAddress, &kr, ^(mach_header_64 mhBuffer) {
+ size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds;
+ withRemoteBuffer(task, dyldAddress, headerPagesSize, false, &kr, ^(void * buffer) {
+ addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, headerPagesSize);
+ });
+ });
+ if (kr != KERN_SUCCESS) {
+ return kr;
}
- addInfoFromLoadCommands((mach_header*)localCopyBuffer, dyldAddress, localCopyBufferSize);
- vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
+
_curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
_curImage++;
return KERN_SUCCESS;
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) {
uint32_t sharedCacheBaseAddress;
uint64_t infoArrayChangeTimestamp;
uint32_t dyldPath;
- uint32_t notifyMachPorts[2];
- uint32_t reserved[11];
+ uint32_t notifyMachPorts[8];
+ uint32_t reserved[5];
+ uint32_t compact_dyld_image_info_addr;
+ uint32_t compact_dyld_image_info_size;
};
struct dyld_all_image_infos_64 {
uint64_t sharedCacheBaseAddress;
uint64_t infoArrayChangeTimestamp;
uint64_t dyldPath;
- uint32_t notifyMachPorts[2];
- uint64_t reserved[12];
+ uint32_t notifyMachPorts[8];
+ uint64_t reserved[9];
+ uint64_t compact_dyld_image_info_addr;
+ uint64_t compact_dyld_image_info_size;
};
struct dyld_image_info_32 {
#include "dyld_images.h"
#include "dyld_priv.h"
+#include "LaunchCache.h"
+#include "Loading.h"
+#include "AllImages.h"
+
+
typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
typedef void (^NotifyExit)();
typedef void (^NotifyMain)();
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();
+ ~dyld_process_info_notify_base();
uint32_t& retainCount() const { return _retainCount; }
void setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
+ // override new and delete so we don't need to link with libc++
+ static void* operator new(size_t sz) { return malloc(sz); }
+ static void operator delete(void* p) { return free(p); }
+
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;
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);
+ dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
if ( kern_return_t r = obj->makePorts() ) {
if ( kr != NULL )
return obj;
fail:
- free(obj);
+ delete obj;
return NULL;
}
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);
- }
+ if ( object->retainCount() == 0 )
+ delete object;
+}
+
+
+
+
+
+
+
+namespace dyld3 {
+
+
+static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+
+static void notifyMonitoringDyld(bool unloading, unsigned portSlot, const launch_cache::DynArray<loader::ImageInfo>& imageInfos)
+{
+ if ( sZombieNotifiers[portSlot] )
+ return;
+
+ unsigned entriesSize = (unsigned)imageInfos.count()*sizeof(dyld_process_info_image_entry);
+ unsigned pathsSize = 0;
+ for (uintptr_t i=0; i < imageInfos.count(); ++i) {
+ launch_cache::Image image(imageInfos[i].imageData);
+ pathsSize += (strlen(image.path()) + 1);
+ }
+ unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + 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 = (unsigned)imageInfos.count()/2;
+ const launch_cache::DynArray<loader::ImageInfo> firstHalf(imageHalfCount, (loader::ImageInfo*)&imageInfos[0]);
+ const launch_cache::DynArray<loader::ImageInfo> secondHalf(imageInfos.count() - imageHalfCount, (loader::ImageInfo*)&imageInfos[imageHalfCount]);
+ notifyMonitoringDyld(unloading, portSlot, firstHalf);
+ notifyMonitoringDyld(unloading, portSlot, secondHalf);
+ return;
+ }
+ // build buffer to send
+ dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+ uint8_t buffer[totalSize];
+ dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
+ header->version = 1;
+ header->imageCount = (uint32_t)imageInfos.count();
+ header->imagesOffset = sizeof(dyld_process_info_notify_header);
+ header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize;
+ header->timestamp = allImageInfo->infoArrayChangeTimestamp;
+ 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 (uintptr_t i=0; i < imageInfos.count(); ++i) {
+ launch_cache::Image image(imageInfos[i].imageData);
+ strcpy(pathPool, image.path());
+ uint32_t len = (uint32_t)strlen(pathPool);
+ memcpy(entries->uuid, image.uuid(), sizeof(uuid_t));
+ entries->loadAddress = (uint64_t)imageInfos[i].loadAddress;
+ entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
+ entries->pathLength = len;
+ pathPool += (len +1);
+ ++entries;
+ }
+ // lazily alloc reply port
+ 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);
+ //log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
+ }
+ //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 = allImageInfo->notifyPorts[portSlot];
+ h->msgh_reserved = 0;
+ h->msgh_size = (mach_msg_size_t)sizeof(buffer);
+ //log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, allImageInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
+ kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 2000, MACH_PORT_NULL);
+ //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
+ //log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
+ mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[portSlot]);
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+ allImageInfo->notifyPorts[portSlot] = 0;
+ sNotifyReplyPorts[portSlot] = 0;
+ }
+ else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+ // client took too long, ignore him from now on
+ sZombieNotifiers[portSlot] = true;
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+ sNotifyReplyPorts[portSlot] = 0;
+ }
}
+void AllImages::notifyMonitorMain()
+{
+ dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( (allImageInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
+ if ( sNotifyReplyPorts[slot] == 0 ) {
+ if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
+ mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
+ //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
+ }
+ //dyld::log("found port to send to\n");
+ uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+ mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+ 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 = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
+ h->msgh_local_port = sNotifyReplyPorts[slot];
+ h->msgh_remote_port = allImageInfo->notifyPorts[slot];
+ h->msgh_reserved = 0;
+ h->msgh_size = (mach_msg_size_t)sizeof(messageBuffer);
+ //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, allImageInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id);
+ kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 2000, 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", allImageInfo->notifyPorts[slot], sNotifyReplyPorts[slot]);
+ mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[slot]);
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+ allImageInfo->notifyPorts[slot] = 0;
+ sNotifyReplyPorts[slot] = 0;
+ }
+ else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+ // client took too long, ignore him from now on
+ sZombieNotifiers[slot] = true;
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+ sNotifyReplyPorts[slot] = 0;
+ }
+ }
+ }
+}
+
+void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+{
+ // notify each monitoring process
+ dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( allImageInfo->notifyPorts[slot] != 0 ) {
+ notifyMonitoringDyld(false, slot, newImages);
+ }
+ 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;
+ sZombieNotifiers[slot] = false;
+ }
+ }
+}
+
+void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
+{
+ // notify each monitoring process
+ dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( allImageInfo->notifyPorts[slot] != 0 ) {
+ notifyMonitoringDyld(true, slot, unloadingImages);
+ }
+ 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;
+ sZombieNotifiers[slot] = false;
+ }
+ }
+}
+
+} // namespace dyld3
+
+
_ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
}
+// std::__terminate() called by C++ unwinding code
+void _ZSt11__terminatePFvvE(void (*func)())
+{
+ _ZN4dyld4haltEPKc("dyld std::__terminate()\n");
+}
+
+// std::__unexpected() called by C++ unwinding code
+void _ZSt12__unexpectedPFvvE(void (*func)())
+{
+ _ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
+}
+
+// terminate_handler get_terminate()
+void* _ZSt13get_terminatev()
+{
+ return NULL;
+}
+
+// unexpected_handler get_unexpected()
+void* _ZSt14get_unexpectedv()
+{
+ return NULL;
+}
+
+// new_handler get_new_handler()
+void* _ZSt15get_new_handlerv()
+{
+ return NULL;
+}
+
+
+
// __cxxabiv1::__terminate_handler
void* _ZN10__cxxabiv119__terminate_handlerE = &_ZSt9terminatev;
va_end(list);
}
+#if __i386__
+void _ZN4dyld4vlogEPKcPc(const char* format, va_list list) {
+#else
+void _ZN4dyld4vlogEPKcP13__va_list_tag(const char* format, va_list list) {
+#endif
+ gSyscallHelpers->vlog(format, list);
+}
+
+
+
void _ZN4dyld4warnEPKcz(const char* format, ...) {
va_list list;
va_start(list, format);
return KERN_NOT_SUPPORTED;
}
+kern_return_t task_info(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt) {
+ if ( gSyscallHelpers->version >= 8 )
+ return gSyscallHelpers->task_info(target_task, flavor, task_info_out, task_info_outCnt);
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t thread_info(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt) {
+ if ( gSyscallHelpers->version >= 8 )
+ return gSyscallHelpers->task_info(target_act, flavor, thread_info_out, thread_info_outCnt);
+ return KERN_NOT_SUPPORTED;
+}
+
+bool kdebug_is_enabled(uint32_t code) {
+ if ( gSyscallHelpers->version >= 8 )
+ return gSyscallHelpers->kdebug_is_enabled(code);
+ return false;
+}
+
+int kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
+ if ( gSyscallHelpers->version >= 8 )
+ return gSyscallHelpers->kdebug_trace(code, arg1, arg2, arg3, arg4);
+ return 0;
+}
+
int* __error(void) {
return gSyscallHelpers->errnoAddress();
}
#endif
+void* _NSConcreteStackBlock[32];
+void* _NSConcreteGlobalBlock[32];
+
+void _Block_object_assign()
+{
+ _ZN4dyld4haltEPKc("_Block_object_assign()");
+}
+
+void _Block_object_dispose(const void* object, int flags)
+{
+ // only support stack blocks in dyld: BLOCK_FIELD_IS_BYREF=8
+ if ( flags != 8 )
+ _ZN4dyld4haltEPKc("_Block_object_dispose()");
+}
+
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
+ // no thread local storage in image: should never happen
+ if ( size == 0 )
+ return NULL;
// allocate buffer and fill with template
void* buffer = malloc(size);
}
-void tlv_load_notification(const struct mach_header* mh, intptr_t slide)
+static 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.
pthread_setspecific(tlv_terminators_key, list);
}
else {
- if ( list->allocCount == list->allocCount ) {
+ if ( list->useCount == list->allocCount ) {
// handle resizing allocation
uint32_t newAllocCount = list->allocCount * 2;
size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]);
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", "/")
+ dyldSrcDir = os.getenv("SRCROOT", "")
+ if not dyldSrcDir:
+ dyldSrcDir = os.getcwd()
+ testsSrcTopDir = dyldSrcDir + "/testing/test-cases/"
+ sdkDir = os.getenv("SDKROOT", "")
+ if not sdkDir:
+ #sdkDir = subprocess.check_output(["xcrun", "--show-sdk-path"]).rstrip()
+ sdkDir = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.Internal.sdk"
toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
defaultMinOS = ""
+ minVersNum = "10.12"
minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
if minOSOption:
minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
if minOSVersName:
minVersNum = os.getenv(minOSVersName, "")
+ else:
+ minOSOption = "mmacosx-version-min"
platformName = os.getenv("PLATFORM_NAME", "osx")
archOptions = ""
archList = os.getenv("RC_ARCHS", "")
elif platformName == "appletvos":
archOptions = "-arch arm64"
else:
- archOptions = ""
- for arch in string.split(archList, " "):
- archOptions = archOptions + " -arch " + arch
+ if archList:
+ for arch in string.split(archList, " "):
+ archOptions = archOptions + " -arch " + arch
+ else:
+ archOptions = "-arch x86_64"
allTests = []
for f in os.listdir(testsSrcTopDir):
if f.endswith(".dtest"):
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations
+
+// RUN: ./NSAddImage-fail.exe return
+// RUN: NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH ./NSAddImage-fail.exe abort
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+ const char* arg = argv[1];
+
+ if ( strcmp(arg, "return") == 0 ) {
+ printf("[BEGIN] NSAddImage-fail %s\n", arg);
+ const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+ if ( mh == NULL )
+ printf("[PASS] NSAddImage-fail %s\n", arg);
+ else
+ printf("[FAIL] NSAddImage-fail %s\n", arg);
+ }
+ else {
+ // run with nocr which print BEGIN/PASS/FAIL
+ NSAddImage("/xqz/42/libnotfound.xxx", 0);
+ }
+
+ return 0;
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations
+
+// RUN: ./NSAddImage-loaded.exe return
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSAddImage-loaded\n");
+
+ // verify value is returned for image already loaded
+ const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+ if ( mh == NULL )
+ printf("[FAIL] NSAddImage-loaded\n");
+
+ // verify existing dylib is not loaded if it is not already loaded
+ mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+ if ( mh != NULL )
+ printf("[FAIL] NSAddImage-loaded\n");
+
+ printf("[PASS] NSAddImage-loaded\n");
+
+ return 0;
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations
+
+// RUN: ./NSAddressOfSymbol-basic.exe
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+extern struct mach_header __dso_handle;
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSAddressOfSymbol-basic\n");
+
+ NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ if ( sym == NULL ) {
+ printf("[FAIL] NSAddressOfSymbol-basic can't find main\n");
+ return 0;
+ }
+ void* mainAddr = NSAddressOfSymbol(sym);
+ if ( mainAddr != &main ) {
+ printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main);
+ return 0;
+ }
+
+ // verify NULL works
+ if ( NSAddressOfSymbol(NULL) != NULL ) {
+ printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n");
+ return 0;
+ }
+
+ printf("[PASS] NSAddressOfSymbol-basic\n");
+ return 0;
+}
+
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations
+// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN: ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+
+ const char* path = argv[1];
+
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+ printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
+ return 0;
+ }
+
+ NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ printf("[FAIL] NSLinkModule failed\n");
+ return 0;
+ }
+
+ NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+ if ( sym == NULL ) {
+ printf("[FAIL] NSLookupSymbolInModule failed\n");
+ return 0;
+ }
+
+ void* func = NSAddressOfSymbol(sym);
+ if ( func == NULL ) {
+ printf("[FAIL] NSAddressOfSymbol failed\n");
+ return 0;
+ }
+
+ Dl_info info;
+ if ( dladdr(func, &info) == 0 ) {
+ printf("[FAIL] dladdr(&p, xx) failed");
+ return 0;
+ }
+ //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ printf("[FAIL] NSUnLinkModule failed\n");
+ return 0;
+ }
+
+ if ( dladdr(func, &info) != 0 ) {
+ printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+ return 0;
+ }
+
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ return 0;
+ }
+
+ printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
+ return 0;
+}
+
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
+// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN: ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+static void checkBundle(const char* path, bool unlinkBeforeDestroy)
+{
+ int fd = open(path, O_RDONLY, 0);
+ if ( fd == -1 ) {
+ printf("[FAIL] open(%s) failed", path);
+ exit(0);
+ }
+
+ struct stat stat_buf;
+ if ( fstat(fd, &stat_buf) == -1) {
+ printf("[FAIL] fstat() failed\n");
+ exit(0);
+ }
+
+ void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( loadAddress == ((void*)(-1)) ) {
+ printf("[FAIL] mmap() failed\n");
+ exit(0);
+ }
+
+ close(fd);
+
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
+ printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
+ exit(0);
+ }
+
+ NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ printf("[FAIL] NSLinkModule failed\n");
+ exit(0);
+ }
+
+ if ( !unlinkBeforeDestroy ) {
+ // API lets you destroy ofi and NSModule lives on
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ exit(0);
+ }
+ }
+
+ NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+ if ( sym == NULL ) {
+ printf("[FAIL] NSLookupSymbolInModule failed\n");
+ exit(0);
+ }
+
+ void* func = NSAddressOfSymbol(sym);
+ if ( func == NULL ) {
+ printf("[FAIL] NSAddressOfSymbol failed\n");
+ exit(0);
+ }
+
+ Dl_info info;
+ if ( dladdr(func, &info) == 0 ) {
+ printf("[FAIL] dladdr(&p, xx) failed\n");
+ exit(0);
+ }
+ //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ printf("[FAIL] NSUnLinkModule failed\n");
+ exit(0);
+ }
+
+ if ( dladdr(func, &info) != 0 ) {
+ printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+ exit(0);
+ }
+
+ if ( unlinkBeforeDestroy ) {
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ exit(0);
+ }
+ }
+}
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n");
+
+ checkBundle(argv[1], true);
+ checkBundle(argv[1], false);
+
+ // Now go again enough times to flush out any limits in our dlopen encodings.
+ for (unsigned i = 0; i != 255; ++i)
+ checkBundle(argv[1], false);
+
+ printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n");
+ return 0;
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations
+
+// RUN: ./NSLookupSymbolInImage-basic.exe
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+extern struct mach_header __dso_handle;
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSLookupSymbolInImage-basic\n");
+
+ // verify known symbol works
+ NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ if ( sym == NULL ) {
+ printf("[FAIL] NSLookupSymbolInImage-basic _main\n");
+ return 0;
+ }
+
+ // verify mode where NSLookupSymbolInImage() returns NULL if symbol not found
+ sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ if ( sym != NULL ) {
+ printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n");
+ return 0;
+ }
+
+ // Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used,
+ // but dyld 2 just returned NULL, so no need to test that.
+
+ printf("[PASS] NSLookupSymbolInImage-basic\n");
+ return 0;
+}
+
--- /dev/null
+const char* bar()
+{
+ return "bar";
+}
--- /dev/null
+const char* foo()
+{
+ return "foo";
+}
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dyld_immutable_test.exe
+
+// RUN: ./dyld_immutable_test.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+typedef const char* (*BarProc)(void);
+
+extern uint32_t _cpu_capabilities;
+extern const char* foo();
+
+const char* myStr = "myStr";
+
+int myInt;
+
+
+int main()
+{
+ printf("[BEGIN] _dyld_is_memory_immutable\n");
+
+ if ( !_dyld_is_memory_immutable(myStr, 6) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned false for string in main executable\n");
+ return 0;
+ }
+
+ if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned true for result from strdup()\n");
+ return 0;
+ }
+
+ if ( _dyld_is_memory_immutable(&myInt, 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned true for global variabe in main executable\n");
+ return 0;
+ }
+
+ if ( !_dyld_is_memory_immutable(foo(), 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned false for string in statically linked dylib\n");
+ return 0;
+ }
+
+ if ( !_dyld_is_memory_immutable(&strcpy, 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned false for function in dyld shared cache\n");
+ return 0;
+ }
+
+ if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned true for global variable in shared cache\n");
+ return 0;
+ }
+
+ void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen(libbar.dylib) failed");
+ return 0;
+ }
+
+ BarProc proc = dlsym(handle, "bar");
+ if ( proc == NULL ) {
+ printf("[FAIL] dlsym(bar) failed\n");
+ return 0;
+ }
+ const char* barStr = (*proc)();
+ if ( _dyld_is_memory_immutable(barStr, 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned true for string in unloadable dylib\n");
+ return 0;
+ }
+
+
+ printf("[PASS] _dyld_is_memory_immutable\n");
+ return 0;
+}
+
--- /dev/null
+void foo() {}
--- /dev/null
+
+// BUILD: $CXX main.cxx -o $BUILD_DIR/dyld_register_test.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+
+// RUN: ./dyld_register_test.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+#include <unordered_set>
+
+extern mach_header __dso_handle;
+
+static std::unordered_set<const mach_header*> sCurrentImages;
+
+static void notify(const mach_header* mh, intptr_t vmaddr_slide)
+{
+ //fprintf(stderr, "mh=%p\n", mh);
+ if ( sCurrentImages.count(mh) != 0 ) {
+ printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh);
+ exit(0);
+ }
+ sCurrentImages.insert(mh);
+}
+
+
+int main()
+{
+ printf("[BEGIN] _dyld_register_func_for_add_image\n");
+
+ _dyld_register_func_for_add_image(¬ify);
+
+ // verify we were notified about already loaded images
+ if ( sCurrentImages.count(&__dso_handle) == 0 ) {
+ printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about main executable");
+ exit(0);
+ }
+ const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
+ if ( sCurrentImages.count(libSysMH) == 0 ) {
+ printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libsystem_c.dylib");
+ exit(0);
+ }
+
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
+ exit(0);
+ }
+
+ void* sym = dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ printf("[FAIL] dlsym(handle, \"foo\") failed");
+ exit(0);
+ }
+
+ // verify we were notified about load of libfoo.dylib
+ const mach_header* libfooMH = dyld_image_header_containing_address(sym);
+ if ( sCurrentImages.count(libfooMH) == 0 ) {
+ printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libfoo.dylib");
+ exit(0);
+ }
+
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("[FAIL] dlclose(handle) returned %d", result);
+ exit(0);
+ }
+
+
+ printf("[PASS] _dyld_register_func_for_add_image\n");
+ return 0;
+}
+
Dl_info info;
if ( dladdr(&main, &info) == 0 ) {
- printf("[FAIL] dladdr(&main, xx) failed");
+ printf("[FAIL] dladdr(&main, xx) failed\n");
return 0;
}
if ( info.dli_sname != NULL ){
- printf("[FAIL] dladdr() returned: \"%s\" instead of NULL", info.dli_sname);
+ printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname);
return 0;
}
{
Dl_info info;
if ( dladdr(&bar, &info) == 0 ) {
- printf("[FAIL] dladdr(&bar, xx) failed");
+ printf("[FAIL] dladdr(&bar, xx) failed\n");
exit(0);
}
if ( strcmp(info.dli_sname, "bar") != 0 ) {
- printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname);
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
exit(0);
}
if ( info.dli_saddr != &bar) {
- printf("[FAIL] dladdr()->dli_saddr is not &bar");
+ printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
exit(0);
}
if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) {
- printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar");
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
exit(0);
}
}
{
Dl_info info;
if ( dladdr(&foo, &info) == 0 ) {
- printf("[FAIL] dladdr(&foo, xx) failed");
+ printf("[FAIL] dladdr(&foo, xx) failed\n");
exit(0);
}
if ( strcmp(info.dli_sname, "foo") != 0 ) {
- printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname);
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
exit(0);
}
if ( info.dli_saddr != &foo) {
- printf("[FAIL] dladdr()->dli_saddr is not &foo");
+ printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
exit(0);
}
if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) {
- printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo");
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
exit(0);
}
}
{
Dl_info info;
if ( dladdr(&hide, &info) == 0 ) {
- printf("[FAIL] dladdr(&hide, xx) failed");
+ printf("[FAIL] dladdr(&hide, xx) failed\n");
exit(0);
}
if ( strcmp(info.dli_sname, "hide") != 0 ) {
- printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname);
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
exit(0);
}
if ( info.dli_saddr != &hide) {
- printf("[FAIL] dladdr()->dli_saddr is not &hide");
+ printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
exit(0);
}
if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) {
- printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide");
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
exit(0);
}
}
{
Dl_info info;
if ( dladdr(&malloc, &info) == 0 ) {
- printf("[FAIL] dladdr(&malloc, xx) failed");
+ printf("[FAIL] dladdr(&malloc, xx) failed\n");
exit(0);
}
if ( strcmp(info.dli_sname, "malloc") != 0 ) {
- printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname);
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
exit(0);
}
if ( info.dli_saddr != &malloc) {
- printf("[FAIL] dladdr()->dli_saddr is not &malloc");
+ printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
exit(0);
}
if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
- printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc");
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
exit(0);
}
}
--- /dev/null
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+int dylib_bar()
+{
+ return 2;
+}
+
+static int dylib_foo()
+{
+ return 3;
+}
+
+__attribute__((visibility("hidden"))) int dylib_hide()
+{
+ return 4;
+}
+
+// checks global symbol
+static void verifybar()
+{
+ Dl_info info;
+ if ( dladdr(&dylib_bar, &info) == 0 ) {
+ printf("[FAIL] dladdr(&dylib_bar, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "dylib_bar") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &dylib_bar) {
+ printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_bar\n");
+ exit(0);
+ }
+}
+
+// checks local symbol
+static void verifyfoo()
+{
+ Dl_info info;
+ if ( dladdr(&dylib_foo, &info) == 0 ) {
+ printf("[FAIL] dladdr(&dylib_foo, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "dylib_foo") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &dylib_foo) {
+ printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_foo\n");
+ exit(0);
+ }
+}
+
+// checks hidden symbol
+static void verifyhide()
+{
+ Dl_info info;
+ if ( dladdr(&dylib_hide, &info) == 0 ) {
+ printf("[FAIL] dladdr(&dylib_hide, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "dylib_hide") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &dylib_hide) {
+ printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_hide\n");
+ exit(0);
+ }
+}
+
+
+void verifyDylib()
+{
+ verifybar();
+ verifyfoo();
+ verifyhide();
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dladdr-basic.exe
+
+// RUN: ./dladdr-basic.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+extern void verifyDylib();
+
+int bar()
+{
+ return 2;
+}
+
+static int foo()
+{
+ return 3;
+}
+
+__attribute__((visibility("hidden"))) int hide()
+{
+ return 4;
+}
+
+// checks global symbol
+static void verifybar()
+{
+ Dl_info info;
+ if ( dladdr(&bar, &info) == 0 ) {
+ printf("[FAIL] dladdr(&bar, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "bar") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &bar) {
+ printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
+ exit(0);
+ }
+}
+
+// checks local symbol
+static void verifyfoo()
+{
+ Dl_info info;
+ if ( dladdr(&foo, &info) == 0 ) {
+ printf("[FAIL] dladdr(&foo, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "foo") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &foo) {
+ printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
+ exit(0);
+ }
+}
+
+// checks hidden symbol
+static void verifyhide()
+{
+ Dl_info info;
+ if ( dladdr(&hide, &info) == 0 ) {
+ printf("[FAIL] dladdr(&hide, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "hide") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &hide) {
+ printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != &__dso_handle ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
+ exit(0);
+ }
+}
+
+// checks dylib symbol
+static void verifymalloc()
+{
+ Dl_info info;
+ if ( dladdr(&malloc, &info) == 0 ) {
+ printf("[FAIL] dladdr(&malloc, xx) failed\n");
+ exit(0);
+ }
+ if ( strcmp(info.dli_sname, "malloc") != 0 ) {
+ printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
+ exit(0);
+ }
+ if ( info.dli_saddr != &malloc) {
+ printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
+ exit(0);
+ }
+ if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
+ printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
+ exit(0);
+ }
+}
+
+
+int main()
+{
+ printf("[BEGIN] dladdr-basic\n");
+ verifybar();
+ verifyhide();
+ verifyfoo();
+ verifymalloc();
+
+ verifyDylib();
+
+ printf("[PASS] dladdr-basic\n");
+ return 0;
+}
+
--- /dev/null
+int bar()
+{
+ return VALUE;
+}
+
--- /dev/null
+extern int bar();
+
+int foo()
+{
+ return bar() + VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10 $BUILD_DIR/door1/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=25 $BUILD_DIR/door2/libbar.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door1/ ./main.exe 13
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door2 ./main.exe 42
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door3/:$RUN_DIR/door2/ ./main.exe 42
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+// Program dlopen()s libfoo.dylib which was linked against libbar.dylib
+// Neither have valid paths and must be found via DYLD_LIBRARY_PATH
+// This test direct and indirect loading.
+
+int main(int argc, const char* argv[])
+{
+ const char* env = getenv("DYLD_LIBRARY_PATH");
+ if ( env == NULL ) {
+ printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n");
+ return 0;
+ }
+ const char* valueStr = argv[1];
+ if ( valueStr == NULL ) {
+ printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n");
+ return 0;
+ }
+ char* end;
+ long value = strtol(valueStr, &end, 0);
+
+ printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+
+ void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ return 0;
+ }
+
+ typedef int (*FooProc)();
+
+ FooProc sym = (FooProc)dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ return 0;
+ }
+
+ int result = (*sym)();
+ if ( result != value ) {
+ printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr);
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ return 0;
+ }
+
+ int r = dlclose(handle);
+ if ( r != 0 ) {
+ printf("dlclose() returned %d\n", r);
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ return 0;
+ }
+
+ void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
+ if ( handle2 == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ return 0;
+ }
+
+
+
+ printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: cp bad.txt $BUILD_DIR/libnota.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-bad-file.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-bad-file\n");
+
+ // try to dlopen() a text file
+ void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
+ if ( handle != NULL ) {
+ printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib");
+ return 0;
+ }
+ const char* message = dlerror();
+ if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) {
+ printf("dlerror: %s\n", message);
+ printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n");
+ return 0;
+ }
+
+ // try to dlopen() a directory
+ handle = dlopen(RUN_DIR, RTLD_FIRST);
+ if ( handle != NULL ) {
+ printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR);
+ return 0;
+ }
+ message = dlerror();
+ if ( strstr(message, "not a file") == NULL ) {
+ printf("dlerror: %s\n", message);
+ printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n");
+ return 0;
+ }
+
+ printf("[PASS] dlopen-bad-file\n");
+
+ return 0;
+}
+
--- /dev/null
+
+int dummy;
+
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlopen-empty-data.exe -DRUN_DIR="$RUN_DIR"
+
+
+// RUN: ./dlopen-empty-data.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
+
+int main()
+{
+ printf("[BEGIN] dlopen-empty-data\n");
+
+ void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror());
+ return 0;
+ }
+
+ printf("[PASS] dlopen-empty-data\n");
+ return 0;
+}
+
--- /dev/null
+
+extern int foo();
+
+extern int gInitialisersCalled;
+
+__attribute__((constructor))
+static void onLoad() {
+ ++gInitialisersCalled;
+}
+
+typedef int(*retTy)();
+
+retTy bar() {
+ return &foo;
+}
\ No newline at end of file
--- /dev/null
+
+
+extern int gInitialisersCalled;
+
+__attribute__((constructor))
+static void onLoad() {
+ ++gInitialisersCalled;
+}
+
+
+int foo() {
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -Wl,-U,_gInitialisersCalled -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC bar.c -dynamiclib -Wl,-U,_gInitialisersCalled $BUILD_DIR/libfoo.dylib -flat_namespace -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-flat.exe
+
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR ./dlopen-flat.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int gInitialisersCalled = 0;
+
+int main() {
+ printf("[BEGIN] dlopen-flat\n");
+ int result;
+
+ // Foo exports foo()
+ void* fooHandle = 0;
+ {
+ const char* path = RUN_DIR "/libfoo.dylib";
+ fooHandle = dlopen(path, RTLD_LAZY);
+ if (!fooHandle) {
+ printf("dlopen failed with error: %s\n", dlerror());
+ return 1;
+ }
+ if (gInitialisersCalled != 1) {
+ printf("gInitialisersCalled != 1\n");
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+ }
+ // Now unload foo which should do something.
+ result = dlclose(fooHandle);
+ if (result != 0) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+
+ // Open foo again which should do something.
+ {
+ const char* path = RUN_DIR "/libfoo.dylib";
+ fooHandle = dlopen(path, RTLD_LAZY);
+ if (!fooHandle) {
+ printf("dlopen failed with error: %s\n", dlerror());
+ return 1;
+ }
+ if (gInitialisersCalled != 2) {
+ printf("gInitialisersCalled != 2\n");
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+ }
+
+ // Bar is going to resolve foo()
+ void* barHandle = 0;
+ {
+ const char* path = RUN_DIR "/libbar.dylib";
+ barHandle = dlopen(path, RTLD_LAZY);
+ if (!barHandle) {
+ printf("dlopen failed with error: %s\n", dlerror());
+ return 1;
+ }
+ if (gInitialisersCalled != 3) {
+ printf("gInitialisersCalled != 3\n");
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+ }
+ // Now unload foo which shouldn't do anything.
+ result = dlclose(fooHandle);
+ if (result != 0) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+
+ // Open foo again which shouldn't do anything.
+ {
+ const char* path = RUN_DIR "/libfoo.dylib";
+ fooHandle = dlopen(path, RTLD_LAZY);
+ if (!fooHandle) {
+ printf("dlopen failed with error: %s\n", dlerror());
+ return 1;
+ }
+ if (gInitialisersCalled != 3) {
+ printf("gInitialisersCalled != 3\n");
+ printf("[FAIL] dlopen-flat\n");
+ return 1;
+ }
+ }
+
+ printf("[PASS] dlopen-flat\n");
+ return 0;
+}
+
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+void barInDylib()
+{
+}
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+extern void barInDylib();
+
+void bazInDylib()
+{
+ return barInDylib();
+}
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations
+// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC baz.c -dynamiclib -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib
+
+// RUN: ./dlopen-indirect-groupNum.exe $RUN_DIR/foo.bundle $RUN_DIR/libbar.dylib $RUN_DIR/libbaz.dylib
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+static void checkBundle(const char* path, bool unlinkBeforeDestroy)
+{
+ int fd = open(path, O_RDONLY, 0);
+ if ( fd == -1 ) {
+ printf("[FAIL] open(%s) failed", path);
+ exit(0);
+ }
+
+ struct stat stat_buf;
+ if ( fstat(fd, &stat_buf) == -1) {
+ printf("[FAIL] fstat() failed\n");
+ exit(0);
+ }
+
+ void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( loadAddress == ((void*)(-1)) ) {
+ printf("[FAIL] mmap() failed\n");
+ exit(0);
+ }
+
+ close(fd);
+
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
+ printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
+ exit(0);
+ }
+
+ NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ printf("[FAIL] NSLinkModule failed\n");
+ exit(0);
+ }
+
+ if ( !unlinkBeforeDestroy ) {
+ // API lets you destroy ofi and NSModule lives on
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ exit(0);
+ }
+ }
+
+ NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+ if ( sym == NULL ) {
+ printf("[FAIL] NSLookupSymbolInModule failed\n");
+ exit(0);
+ }
+
+ void* func = NSAddressOfSymbol(sym);
+ if ( func == NULL ) {
+ printf("[FAIL] NSAddressOfSymbol failed\n");
+ exit(0);
+ }
+
+ Dl_info info;
+ if ( dladdr(func, &info) == 0 ) {
+ printf("[FAIL] dladdr(&p, xx) failed\n");
+ exit(0);
+ }
+ //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ printf("[FAIL] NSUnLinkModule failed\n");
+ exit(0);
+ }
+
+ if ( dladdr(func, &info) != 0 ) {
+ printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+ exit(0);
+ }
+
+ if ( unlinkBeforeDestroy ) {
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ exit(0);
+ }
+ }
+}
+
+
+
+static void tryImage(const char* path, const char* symbol)
+{
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+ exit(0);
+ }
+
+ void* sym = dlsym(handle, symbol);
+ if ( sym == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+ exit(0);
+ }
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("dlclose() returned %c\n", result);
+ printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+ exit(0);
+ }
+}
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] dlopen-indirect-groupNum\n");
+
+ checkBundle(argv[1], true);
+ checkBundle(argv[1], false);
+
+ // Now go again enough times to flush out any limits in our dlopen encodings.
+ for (unsigned i = 0; i != 255; ++i)
+ checkBundle(argv[1], false);
+
+ // Open bar.dylib
+ tryImage(argv[2], "barInDylib");
+
+ // And now open baz.dylib which depends on bar.dylib
+ tryImage(argv[3], "bazInDylib");
+
+ printf("[PASS] dlopen-indirect-groupNum\n");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+
+
+void a(const char* from) {
+ char buffer[100];
+ sprintf(buffer, "a() from %s", from);
+ setState(buffer);
+}
--- /dev/null
+extern void c(const char*);
+
+void b() { }
+
+void __attribute__((constructor))
+initB()
+{
+ c("initB");
+}
+
+
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+
+void c(const char* from) {
+ char buffer[100];
+ sprintf(buffer, "c() from %s", from);
+ setState(buffer);
+}
+
+void __attribute__((constructor))
+initC()
+{
+ setState("initC");
+}
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+extern void c(const char* from);
+
+void d(const char* from) {
+ char buffer[100];
+ sprintf(buffer, "d() from %s", from);
+ setState(buffer);
+}
+
+void __attribute__((constructor))
+initD()
+{
+ c("initD");
+}
+
+
+
--- /dev/null
+extern void a(const char*);
+
+void e() { }
+
+
+void __attribute__((constructor))
+initE()
+{
+ a("initE");
+}
+
+
+
--- /dev/null
+extern void d(const char*);
+
+void f() { }
+
+void __attribute__((constructor))
+initF()
+{
+ d("initF");
+}
+
+
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char* expectedStrings[] = {
+ "a() from main",
+ "initC",
+ "c() from initB",
+ "c() from initD",
+ "a() from initE",
+ "d() from initF",
+ "DONE"
+};
+
+static const char** curState = expectedStrings;
+
+void setState(const char* from)
+{
+ printf("%s\n", from);
+ if ( strcmp(*curState, from) != 0 ) {
+ printf("[FAIL] dlopen-intertwined: expected %s\n", *curState);
+ exit(0);
+ }
+ ++curState;
+}
+
--- /dev/null
+
+
+// BUILD: $CC base.c -dynamiclib -o $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbase.dylib
+// BUILD: $CC A.c -dynamiclib -o $BUILD_DIR/libA.dylib -install_name $RUN_DIR/libA.dylib $BUILD_DIR/libbase.dylib
+// BUILD: $CC C.c -dynamiclib -o $BUILD_DIR/libC.dylib -install_name $RUN_DIR/libC.dylib $BUILD_DIR/libbase.dylib
+// BUILD: $CC B.c -dynamiclib -o $BUILD_DIR/libB.dylib -install_name $RUN_DIR/libB.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib
+// BUILD: $CC D.c -dynamiclib -o $BUILD_DIR/libD.dylib -install_name $RUN_DIR/libD.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib
+// BUILD: $CC E.c -dynamiclib -o $BUILD_DIR/libE.dylib -install_name $RUN_DIR/libE.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib
+// BUILD: $CC F.c -dynamiclib -o $BUILD_DIR/libF.dylib -install_name $RUN_DIR/libF.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libD.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-intertwined.exe $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-intertwined.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+// main deps on A
+// main dlopens B which deps on C
+// main dlopens D which deps on C
+// main dlopens E which deps on A
+// main dlopens F which deps on D
+
+extern void a(const char*);
+extern void setState(const char* from);
+
+int main()
+{
+ printf("[BEGIN] dlopen-intertwined\n");
+
+ a("main");
+
+ void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+ exit(0);
+ }
+
+ handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+ exit(0);
+ }
+
+ handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+ exit(0);
+ }
+
+ handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+ exit(0);
+ }
+
+ setState("DONE");
+
+ printf("[PASS] dlopen-intertwined\n");
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-long-error-message.exe
+
+// RUN: ./dlopen-long-error-message.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-long-error-message\n");
+
+ for (int i=0; i < 10; ++i) {
+ void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST);
+ if ( handle != NULL ) {
+ printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n");
+ return 0;
+ }
+ free(strdup("hello there"));
+ }
+
+ printf("[PASS] dlopen-long-error-message\n");
+
+ return 0;
+}
+
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-race.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-race.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-read\n");
+
+ __block bool allGood = true;
+ dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+ for (int i=0; i < 500; ++i) {
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-read: %s\n", dlerror());
+ exit(0);
+ }
+ dlclose(handle);
+ }
+ });
+
+ printf("[PASS] dlopen-read\n");
+ return 0;
+}
+
--- /dev/null
+int bar()
+{
+ return 0;
+}
+
--- /dev/null
+#include <dlfcn.h>
+
+
+void myinit() __attribute__((constructor));
+
+void myinit()
+{
+ // call dlopen() in initializer
+ void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+}
+
+int foo()
+{
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -DRUN_DIR="$RUN_DIR"
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-recurse.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-recurse.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-recurse\n");
+
+ // libfoo's initializer calls dlopen(). If that hangs, we have a locking bug
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ dlclose(handle);
+
+ printf("[PASS] dlopen-recurse\n");
+ return 0;
+}
+
--- /dev/null
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_DEFAULT.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlsym-RTLD_DEFAULT.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_DEFAULT search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+ void* sym = dlsym(RTLD_DEFAULT, symName);
+ if ( sym == NULL )
+ return false;
+ const char* imagePath = dyld_image_path_containing_address(sym);
+ if ( imagePath == NULL )
+ return false;
+ return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+ printf("[BEGIN] dlsym-RTLD_DEFAULT\n");
+
+ // verify mainSymbol is found in main executable
+ if ( !symbolInImage("mainSymbol", "dlsym-RTLD_DEFAULT") ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: mainSymbol\n");
+ return 0;
+ }
+
+ // verify free is found in main executable, overrideing one in OS
+ if ( !symbolInImage("free", "dlsym-RTLD_DEFAULT") ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: free\n");
+ return 0;
+ }
+
+ // verify foo is found in libfoo-static.dylib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: libfoo-dynamic.dylib could not be loaded\n");
+ return 0;
+ }
+
+ // verify foo is still found in statically linked lib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ // verify foo2 is found in libfoo-dynamic.dylib"
+ if ( !symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: foo2 not in libfoo-dynamic.dylib\n");
+ return 0;
+ }
+
+ // renamed and re-exported symbols work
+ if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
+ printf("[FAIL] dlsym-RTLD_DEFAULT: strcmp not found\n");
+ return 0;
+ }
+
+ printf("[PASS] dlsym-RTLD_DEFAULT\n");
+ return 0;
+}
+
--- /dev/null
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_MAIN_ONLY.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlsym-RTLD_MAIN_ONLY.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_MAIN_ONLY search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+ void* sym = dlsym(RTLD_MAIN_ONLY, symName);
+ if ( sym == NULL )
+ return false;
+ const char* imagePath = dyld_image_path_containing_address(sym);
+ if ( imagePath == NULL )
+ return false;
+ return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+ printf("[BEGIN] dlsym-RTLD_MAIN_ONLY\n");
+
+ // verify mainSymbol is found
+ if ( !symbolInImage("mainSymbol", "dlsym-RTLD_MAIN_ONLY") ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: mainSymbol should have been found\n");
+ return 0;
+ }
+
+ // verify free is found in this program - not in OS
+ if ( !symbolInImage("free", "dlsym-RTLD_MAIN_ONLY") ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: free\n");
+ return 0;
+ }
+
+ // verify foo is not found
+ if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found\n");
+ return 0;
+ }
+
+ void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: libfoo-dynamic.dylib could not be loaded\n");
+ return 0;
+ }
+
+ // verify foo is still not found
+ if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found after dlopen\n");
+ return 0;
+ }
+
+ // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_MAIN_ONLY only searches main executable
+ if ( dlsym(RTLD_MAIN_ONLY, "foo2") != NULL ) {
+ printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo2 found but should not have been\n");
+ return 0;
+ }
+
+ printf("[PASS] dlsym-RTLD_MAIN_ONLY\n");
+ return 0;
+}
+
--- /dev/null
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_NEXT.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlsym-RTLD_NEXT.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_NEXT search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+ void* sym = dlsym(RTLD_NEXT, symName);
+ if ( sym == NULL )
+ return false;
+ const char* imagePath = dyld_image_path_containing_address(sym);
+ if ( imagePath == NULL )
+ return false;
+ return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+ printf("[BEGIN] dlsym-RTLD_NEXT\n");
+
+ // verify mainSymbol is not found
+ if ( dlsym(RTLD_NEXT, "mainSymbol") != NULL ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: mainSymbol should not have been found\n");
+ return 0;
+ }
+
+ // verify free is found in OS (not local one)
+ if ( !symbolInImage("free", "/usr/lib/") ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: free\n");
+ return 0;
+ }
+
+ // verify foo is found in libfoo-static.dylib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: libfoo-dynamic.dylib could not be loaded\n");
+ return 0;
+ }
+
+ // verify foo is still found in statically linked lib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_NEXT only searches thing this image would have seen
+ if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_NEXT: foo2 found but should not have been\n");
+ return 0;
+ }
+
+ printf("[PASS] dlsym-RTLD_NEXT\n");
+ return 0;
+}
+
--- /dev/null
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_SELF.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlsym-RTLD_SELF.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_SELF search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+ void* sym = dlsym(RTLD_SELF, symName);
+ if ( sym == NULL )
+ return false;
+ const char* imagePath = dyld_image_path_containing_address(sym);
+ if ( imagePath == NULL )
+ return false;
+ return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+ printf("[BEGIN] dlsym-RTLD_SELF\n");
+
+ // verify mainSymbol is found
+ if ( dlsym(RTLD_SELF, "mainSymbol") == NULL ) {
+ printf("[FAIL] dlsym-RTLD_SELF: mainSymbol should have been found\n");
+ return 0;
+ }
+
+ // verify free is found in this program - not in OS
+ if ( !symbolInImage("free", "dlsym-RTLD_SELF") ) {
+ printf("[FAIL] dlsym-RTLD_SELF: free\n");
+ return 0;
+ }
+
+ // verify foo is found in libfoo-static.dylib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlsym-RTLD_SELF: libfoo-dynamic.dylib could not be loaded\n");
+ return 0;
+ }
+
+ // verify foo is still found in statically linked lib
+ if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
+ return 0;
+ }
+
+ // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_SELF only searches thing this image would have seen
+ if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+ printf("[FAIL] dlsym-RTLD_SELF: foo2 found but should not have been\n");
+ return 0;
+ }
+
+ printf("[PASS] dlsym-RTLD_SELF\n");
+ return 0;
+}
+
--- /dev/null
+
+void bar() { }
--- /dev/null
+
+void base() { }
+
--- /dev/null
+
+void foo() { }
--- /dev/null
+
+// BUILD: $CC base.c -dynamiclib -install_name $RUN_DIR/libbase.dylib -o $BUILD_DIR/libbase.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC bar.c -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlsym-handle.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlsym-handle.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_DEFAULT search order
+
+int mainSymbol = 4;
+
+int main()
+{
+ printf("[BEGIN] dlsym-handle\n");
+
+ void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if ( fooHandle == NULL ) {
+ printf("[FAIL] dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror());
+ return 0;
+ }
+
+ void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+ if ( barHandle == NULL ) {
+ printf("[FAIL] dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror());
+ return 0;
+ }
+
+ // verify fooHandle does not find mainSymbol
+ if ( dlsym(fooHandle, "mainSymbol") != NULL ) {
+ printf("[FAIL] dlsym-handle: mainSymbol was found with fooHandle\n");
+ return 0;
+ }
+
+ // verify fooHandle can find foo
+ if ( dlsym(fooHandle, "foo") == NULL ) {
+ printf("[FAIL] dlsym-handle: foo not found with fooHandle\n");
+ return 0;
+ }
+
+ // verify fooHandle can find base
+ if ( dlsym(fooHandle, "base") == NULL ) {
+ printf("[FAIL] dlsym-handle: base not found with fooHandle\n");
+ return 0;
+ }
+
+ // verify fooHandle cannot find bar
+ if ( dlsym(fooHandle, "bar") != NULL ) {
+ printf("[FAIL] dlsym-handle: bar found with fooHandle\n");
+ return 0;
+ }
+
+ // verify barHandle can find bar
+ if ( dlsym(barHandle, "bar") == NULL ) {
+ printf("[FAIL] dlsym-handle: bar not found with barHandle\n");
+ return 0;
+ }
+
+ // verify barHandle can find base
+ if ( dlsym(barHandle, "base") == NULL ) {
+ printf("[FAIL] dlsym-handle: base not found with barHandle\n");
+ return 0;
+ }
+
+ // verify barHandle cannot find foo
+ if ( dlsym(barHandle, "foo") != NULL ) {
+ printf("[FAIL] dlsym-handle: foo found with barHandle\n");
+ return 0;
+ }
+
+ // verify renamed and re-exported symbols work
+ if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
+ printf("[FAIL] dlsym-handle: strcmp not found\n");
+ return 0;
+ }
+
+ // verify bad handle errors
+ if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) {
+ printf("[FAIL] dlsym-handle: malloc found with bad handle\n");
+ return 0;
+ }
+ else {
+ const char* message = dlerror();
+ if ( strstr(message, "invalid") == NULL ) {
+ printf("[FAIL] dlsym-handle: invalid handle error message missing 'invalid'\n");
+ return 0;
+ }
+ }
+
+ printf("[PASS] dlsym-handle\n");
+ return 0;
+}
+
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2
+// BUILD: $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib -o $BUILD_DIR/sub1/libsub1.dylib
+// BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib -o $BUILD_DIR/sub2/libsub2.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlsym-reexport.exe -DRUN_DIR="$RUN_DIR" -rpath @loader_path/sub2
+
+// RUN: ./dlsym-reexport.exe
+
+// rpath for sub1 is found in libfoo.dylib. rpath for sub2 is found in dlsym-reexport.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+ printf("[BEGIN] dlsym-re-export\n");
+ // RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports)
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlsym-re-export\n");
+ return 0;
+ }
+
+ void* sym1 = dlsym(handle, "sub1");
+ if ( sym1 == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlsym-re-export\n");
+ return 0;
+ }
+
+ void* sym2 = dlsym(handle, "sub2");
+ if ( sym2 == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlsym-re-export\n");
+ return 0;
+ }
+
+ printf("[PASS] dlsym-re-export\n");
+ return 0;
+}
+
--- /dev/null
+int sub1()
+{
+ return 1;
+}
+
--- /dev/null
+int sub2()
+{
+ return 2;
+}
+
--- /dev/null
+// BUILD: /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h
+// BUILD: $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe
+
+// RUN: $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sdt.h>
+
+#include "probes.h"
+
+int main()
+{
+ printf("[BEGIN] dtrace\n");
+
+ DYLD_TESTING_CALLBACK();
+
+ if (!DYLD_TESTING_CALLBACK_ENABLED())
+ printf("[FAIL] !DYLD_TESTING_CALLBACK_ENABLED\n");
+
+ printf("[PASS] dtrace\n");
+ return 0;
+}
--- /dev/null
+provider dyld_testing {
+ probe callback();
+};
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/sdk-check.exe
+
+// RUN: ./sdk-check.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+
+extern struct mach_header __dso_handle;
+
+int main()
+{
+ printf("[BEGIN] dyld_get_sdk_version\n");
+
+ // should succeed
+ if ( dyld_get_sdk_version(&__dso_handle) == 0 ) {
+ printf("[FAIL] dyld_get_sdk_version: expected SDK\n");
+ return 0;
+ }
+
+ // should fail
+ const char* text = "bad text";
+ if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) {
+ printf("[FAIL] dyld_get_sdk_version: expected failure\n");
+ return 0;
+ }
+
+ printf("[PASS] dyld_get_sdk_version\n");
+
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/dyld_image_path_containing_address-test.exe
+
+// RUN: ./dyld_image_path_containing_address-test.exe
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+
+int main()
+{
+ printf("[BEGIN] dyld_image_path_containing_address-test\n");
+
+ int count = _dyld_image_count();
+ for (int i=0; i < count; ++i) {
+ const struct mach_header* mh = _dyld_get_image_header(i);
+ const char* name1 = _dyld_get_image_name(i);
+ const char* name2 = dyld_image_path_containing_address(mh);
+ if ( strcmp(name1, name2) != 0 ) {
+ printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2);
+ return 0;
+ }
+ }
+
+ printf("[PASS] dyld_image_path_containing_address-test\n");
+ return 0;
+}
+
+#include <signal.h>
+#include <unistd.h>
#include <mach/mach.h>
int main()
{
- task_suspend(mach_task_self());
+ (void)kill(getpid(), SIGSTOP);
return 0;
}
#include <mach/mach.h>
#include <mach/machine.h>
#include <mach-o/dyld_process_info.h>
+#include <Availability.h>
extern char** environ;
cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
#endif
-static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+ pid_t pid;
+ task_t task;
+};
+
+static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
{
- posix_spawnattr_t attr;
+ posix_spawnattr_t attr = 0;
if ( posix_spawnattr_init(&attr) != 0 ) {
printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
exit(0);
}
}
- pid_t childPid;
+ struct task_and_pid child = {0, 0};
const char* argv[] = { testProgPath, NULL };
- int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+ int psResult = posix_spawn(&child.pid, 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);
+ if (posix_spawnattr_destroy(&attr) != 0) {
+ printf("[FAIL] dyld_process_info posix_spawnattr_destroy()\n");
+ exit(0);
+ }
- task_t childTask = 0;
- if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) {
+ if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
printf("[FAIL] dyld_process_info task_for_pid()\n");
- kill(childPid, SIGKILL);
+ kill(child.pid, SIGKILL);
exit(0);
}
+#if __x86_64__
+ //printf("child pid=%d task=%d (%s, %s)\n", child.pid, child.task, launchOtherArch ? "i386" : "x86_64", launchSuspended ? "suspended" : "active");
+#endif
+
// 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);
+ kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
sleep(1);
} while ( info.suspend_count == 0 );
- return childTask;
+ return child;
+}
+
+static void killTest(struct task_and_pid tp) {
+ int r = kill(tp.pid, SIGKILL);
+ waitpid(tp.pid, &r, 0);
}
static bool hasCF(task_t task, bool launchedSuspended)
int main(int argc, const char* argv[])
{
+ kern_return_t kr = KERN_SUCCESS;
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;
+ struct task_and_pid child;
// launch test program same arch as this program
- childTask = launchTest(testProgPath, false, false);
- if ( ! hasCF(childTask, false) ) {
+ child = launchTest(testProgPath, false, false);
+ if ( ! hasCF(child.task, false) ) {
printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
-
+ killTest(child);
+
// launch test program suspended
- childTask = launchTest(testProgPath, false, true);
- if ( ! hasCF(childTask, true) ) {
+ child = launchTest(testProgPath, false, true);
+ if ( ! hasCF(child.task, true) ) {
printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_resume(childTask);
- task_terminate(childTask);
+ (void)kill(child.pid, SIGCONT);
+ killTest(child);
-#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) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // only mac supports multiple architectures, run test program as other arch too
+ child = launchTest(testProgPath, true, false);
+ if ( ! hasCF(child.task, false) ) {
printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n");
- task_terminate(childTask);
+ killTest(child);
+ exit(0);
+ }
+ killTest(child);
+
+ // launch test program suspended
+ child = launchTest(testProgPath, true, true);
+ if ( ! hasCF(child.task, true) ) {
+ printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ (void)kill(child.pid, SIGCONT);
+ killTest(child);
#endif
// verify this program does not use CF
#include <mach/machine.h>
#include <mach-o/dyld_process_info.h>
#include <dispatch/dispatch.h>
+#include <Availability.h>
extern char** environ;
cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
#endif
-static task_t launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+ pid_t pid;
+ task_t task;
+};
+
+static struct task_and_pid 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;
+ posix_spawnattr_t attr = 0;
if ( posix_spawnattr_init(&attr) != 0 ) {
printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
exit(0);
}
}
- pid_t childPid;
+ struct task_and_pid child;
const char* argv[] = { testProgPath, arg1, NULL };
- int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+ int psResult = posix_spawn(&child.pid, 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 ) {
+ if (posix_spawnattr_destroy(&attr) != 0) {
+ printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n");
+ exit(0);
+ }
+ if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
- kill(childPid, SIGKILL);
+ (void)kill(child.pid, SIGKILL);
exit(0);
}
- return childTask;
+ return child;
+}
+
+static void killTest(struct task_and_pid tp) {
+ int r = kill(tp.pid, SIGKILL);
+ waitpid(tp.pid, &r, 0);
}
static void wait_util_task_suspended(task_t task)
}
-static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
+static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate)
{
kern_return_t kr;
__block bool sawMainExecutable = false;
unsigned count = 0;
dyld_process_info_notify handle;
do {
- handle = _dyld_process_info_notify(task, serviceQueue,
+ handle = _dyld_process_info_notify(tp.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;
return false;
}
- // register for notification that it is entrying main()
- _dyld_process_info_notify_main(handle, ^{
- //fprintf(stderr, "target entering main()\n");
- gotMainNotice = true;
- if ( !sawMainExecutable || !sawlibSystem )
- gotMainNoticeBeforeAllInitialDylibs = true;
- });
-
- // if process suspends itself, wait until it has done so
- if ( attachLate )
- wait_util_task_suspended(task);
+ if (!attachLate) {
+ // If the process starts suspended register for main(),
+ // otherwise skip since this test is a race between
+ // process setup and notification registration
+ _dyld_process_info_notify_main(handle, ^{
+ //fprintf(stderr, "target entering main()\n");
+ gotMainNotice = true;
+ if ( !sawMainExecutable || !sawlibSystem )
+ gotMainNoticeBeforeAllInitialDylibs = true;
+ });
+ } else {
+ // if process suspends itself, wait until it has done so
+ wait_util_task_suspended(tp.task);
+ }
// resume from initial suspend
- task_resume(task);
+ kill(tp.pid, SIGCONT);
// block waiting for notification that target has exited
bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
return false;
}
- if ( !attachLate && !sawMainExecutable ) {
- fprintf(stderr, "did not get load notification of main executable\n");
- return false;
- }
+ // Do not run any tests associated with startup unless the kernel suspended us
+ // before main()
+ if (!attachLate) {
+ if ( !sawMainExecutable ) {
+ fprintf(stderr, "did not get load notification of main executable\n");
+ return false;
+ }
- if ( !gotMainNotice ) {
- fprintf(stderr, "did not get notification of main()\n");
- return false;
- }
+ if ( !gotMainNotice ) {
+ fprintf(stderr, "did not get notification of main()\n");
+ return false;
+ }
- if ( gotMainNoticeBeforeAllInitialDylibs ) {
- fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
- return false;
- }
+ if ( gotMainNoticeBeforeAllInitialDylibs ) {
+ fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
+ return false;
+ }
- if ( gotFooNoticeBeforeMain ) {
- fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
- return false;
- }
+ if ( gotFooNoticeBeforeMain ) {
+ fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
+ return false;
+ }
- if ( !attachLate && !sawlibSystem ) {
- fprintf(stderr, "did not get load notification of libSystem\n");
- return false;
+ if ( !sawlibSystem ) {
+ fprintf(stderr, "did not get load notification of libSystem\n");
+ return false;
+ }
}
if ( disconnectEarly ) {
return true;
}
-static void validateMaxNotifies(task_t task)
+static void validateMaxNotifies(struct task_and_pid tp)
{
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,
+ handles[i] = _dyld_process_info_notify(tp.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],
}
else {
fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
- task_terminate(task);
+ killTest(tp);
exit(0);
}
}
const char* testProgPath = argv[1];
dispatch_async(dispatch_get_main_queue(), ^{
- task_t childTask;
+ struct task_and_pid child;
// test 1) launch test program suspended in same arch as this program
- childTask = launchTest(testProgPath, "", false, true);
- if ( ! monitor(childTask, false, false) ) {
+ child = launchTest(testProgPath, "", false, true);
+ if ( ! monitor(child, false, false) ) {
printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ killTest(child);
// 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) ) {
+ child = launchTest(testProgPath, "suspend-in-main", false, false);
+ validateMaxNotifies(child);
+ if ( ! monitor(child, false, true) ) {
printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ killTest(child);
-#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// test 3) launch test program suspended in opposite arch as this program
- childTask = launchTest(testProgPath, "", true, true);
- if ( ! monitor(childTask, false, false) ) {
+ child = launchTest(testProgPath, "", true, true);
+ if ( ! monitor(child, false, false) ) {
printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ killTest(child);
// 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) ) {
+ child = launchTest(testProgPath, "suspend-in-main", true, false);
+ if ( ! monitor(child, false, true) ) {
printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ killTest(child);
#endif
// test 5) launch test program where we disconnect from it after first dlopen
- childTask = launchTest(testProgPath, "", false, true);
- if ( ! monitor(childTask, true, false) ) {
+ child = launchTest(testProgPath, "", false, true);
+ if ( ! monitor(child, true, false) ) {
printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
- task_terminate(childTask);
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
+ killTest(child);
printf("[PASS] dyld_process_info_notify\n");
exit(0);
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
+#include <signal.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());
+ (void)kill(getpid(), SIGSTOP);
for (int i=0; i < 3; ++i) {
void* h = dlopen("./libfoo.dylib", 0);
cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
#endif
-static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+ pid_t pid;
+ task_t task;
+};
+
+static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
{
- posix_spawnattr_t attr;
+ posix_spawnattr_t attr = 0;
if ( posix_spawnattr_init(&attr) != 0 ) {
- printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
+ printf("[FAIL] dyld_process_info_unload 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");
+ printf("[FAIL] dyld_process_info_unload 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");
+ printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
exit(0);
}
}
- pid_t childPid;
+ struct task_and_pid child;
const char* argv[] = { testProgPath, NULL };
- int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+ int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
if ( psResult != 0 ) {
- printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+ printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+ exit(0);
+ }
+ if (posix_spawnattr_destroy(&attr) != 0) {
+ printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
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);
+ if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
+ printf("[FAIL] dyld_process_info_unload task_for_pid()\n");
+ kill(child.pid, SIGKILL);
exit(0);
}
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);
+ kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
sleep(1);
} while ( info.suspend_count == 0 );
- return childTask;
+ return child;
}
-static bool alwaysGetImages(task_t task, bool launchedSuspended)
+static void killTest(struct task_and_pid tp) {
+ int r = kill(tp.pid, SIGKILL);
+ waitpid(tp.pid, &r, 0);
+}
+
+static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended)
{
int failCount = 0;
for (int i=0; i < 100; ++i ) {
kern_return_t result;
- dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+ dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result);
//fprintf(stderr, "info=%p, result=%08X\n", info, result);
if ( i == 0 )
- task_resume(task);
+ (void)kill(tp.pid, SIGCONT);
if ( info == NULL ) {
failCount++;
//fprintf(stderr, "info=%p, result=%08X\n", info, result);
_dyld_process_info_release(info);
}
}
- if ( failCount !=0 ) {
+ // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
+ // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
+ // The important thing is to not crash. Getting NULL back is ok.
+ if ( failCount > 50 ) {
printf("[FAIL] dyld_process_info_unload %d out of 100 calls to _dyld_process_info_create() failed\n", failCount);
return false;
}
exit(0);
}
const char* testProgPath = argv[1];
- task_t childTask;
+ struct task_and_pid child;
// launch test program suspended
- childTask = launchTest(testProgPath, false, true);
- if ( ! alwaysGetImages(childTask, true) ) {
- task_terminate(childTask);
+ child = launchTest(testProgPath, false, true);
+ if ( ! alwaysGetImages(child, true) ) {
+ killTest(child);
exit(0);
}
- task_terminate(childTask);
-
+ killTest(child);
printf("[PASS] dyld_process_info_unload\n");
return 0;
--- /dev/null
+int foo()
+{
+ return VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe $TEMP_DIR/Foo.framework/Foo
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+ printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+
+ if ( foo() == 42 )
+ printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+ else
+ printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+
+ return 0;
+}
--- /dev/null
+int foo()
+{
+ return VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/fallback
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe $TEMP_DIR/libfoo.dylib
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./main.exe
+
+#include <stdio.h>
+
+extern int foo();
+
+int main()
+{
+ printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+
+ if ( foo() == 42 )
+ printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+ else
+ printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+
+ return 0;
+}
+
--- /dev/null
+int foo()
+{
+ return VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe $BUILD_DIR/Frameworks/Foo.framework/Foo
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: ./main.exe
+// RUN: DYLD_FRAMEWORK_PATH=$RUN_DIR/Frameworks-alt/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+ int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1;
+
+ printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected);
+
+ if ( foo() == expected )
+ printf("[PASS] env-DYLD_FRAMEWORK_PATH\n");
+ else
+ printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n");
+
+ return 0;
+}
+
--- /dev/null
+int foo()
+{
+ return VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe $BUILD_DIR/door1/libfoo.dylib
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: ./main.exe
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door2/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+ int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1;
+
+ printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected);
+
+ if ( foo() == expected )
+ printf("[PASS] env-DYLD_LIBRARY_PATH\n");
+ else
+ printf("[FAIL] env-DYLD_LIBRARY_PATH\n");
+
+ return 0;
+}
+
--- /dev/null
+
+#include <stdlib.h>
+#include <string.h>
+
+char buffer[100000];
+char* p = buffer;
+
+void* malloc(size_t size)
+{
+ // bump ptr allocate and fill second half with '#'
+ char* result = p;
+ p += size;
+ memset(result, '#', size);
+ p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc
+ return result;
+}
+
+void free(void* p)
+{
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// 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/flat-namespace.exe -flat_namespace
+
+// RUN: ./flat-namespace.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main()
+{
+ printf("[BEGIN] flat-namespace\n");
+
+ // check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer
+ // <rdar://problem/31921090> strncmp is tricky for flat namespace because it is re-exporte and renamed
+ char* p1 = malloc(10);
+ if ( strncmp(p1, "##########", 10) != 0 ) {
+ printf("[FAIL] malloc() from main executable not interposed\n");
+ return 0;
+ }
+
+ printf("[PASS] flat-namespace\n");
+ return 0;
+}
--- /dev/null
+
+#include <stdlib.h>
+
+void* myalloc1(size_t sz)
+{
+ return malloc(sz);
+}
+
+void* (*pMalloc)(size_t) = &malloc;
+
+void* myalloc2(size_t sz)
+{
+ return (*pMalloc)(sz);
+}
+
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <mach-o/dyld-interposing.h>
+
+
+char buffer[100000];
+char* p = buffer;
+
+void* mymalloc(size_t size)
+{
+ // bump ptr allocate twice the size and fill second half with '#'
+ char* result = p;
+ p += size;
+ memset(p, '#', size);
+ p += size;
+ p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc
+ return result;
+}
+
+void myfree(void* p)
+{
+}
+
+DYLD_INTERPOSE(mymalloc, malloc)
+DYLD_INTERPOSE(myfree, free)
--- /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-malloc.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-malloc.exe
+// BUILD: $CC interposer.c -dynamiclib -o $BUILD_DIR/libmyalloc.dylib -install_name libmyalloc.dylib
+
+// RUN: DYLD_INSERT_LIBRARIES=libmyalloc.dylib ./interpose-malloc.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern void* myalloc1(size_t);
+extern void* myalloc2(size_t);
+
+int main()
+{
+ printf("[BEGIN] interpose-malloc\n");
+
+ char* p1 = malloc(10);
+ if ( strncmp(p1+10, "##########", 10) != 0 ) {
+ printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n");
+ return 0;
+ }
+
+ void* p2 = myalloc1(6);
+ if ( strncmp(p2+6, "######", 6) != 0 ) {
+ printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n");
+ return 0;
+ }
+
+ void* p3 = myalloc2(10);
+ if ( strncmp(p3+10, "##########", 10) != 0 ) {
+ printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n");
+ return 0;
+ }
+
+ void* p4 = strdup("hello");
+ if ( strncmp(p4+6, "#######", 6) != 0 ) {
+ printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n");
+ return 0;
+ }
+
+ //printf("%p %p %p %p\n", p1, p2, p3, p4);
+ printf("[PASS] interpose-malloc\n");
+ return 0;
+}
--- /dev/null
+
+// BUILD: $CXX main.cxx -o $BUILD_DIR/operator-new.exe
+
+// RUN: ./operator-new.exe
+
+#include <stdio.h>
+#include <new>
+
+
+
+//
+// This test case verifies that calling operator new[] in libstdc++.dylib
+// will turn around and call operator new in this main exectuable
+//
+
+static void* ptr;
+
+void* operator new(size_t s) throw (std::bad_alloc)
+{
+ ptr = malloc(s);
+ return ptr;
+}
+
+int main()
+{
+ printf("[BEGIN] operator-new\n");
+
+ char* stuff = new char[24];
+ if ( (void*)stuff == ptr )
+ printf("[PASS] operator-new\n");
+ else
+ printf("[FAIL] operator-new\n");
+
+ return 0;
+}
+
+
--- /dev/null
+
+__thread int a;
+__thread int b = 5;
+
+
+
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libtlv.dylib -o $BUILD_DIR/libtlv.dylib
+// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/thread-local-cleanup.exe
+
+// RUN: ./thread-local-cleanup.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+
+
+
+int main()
+{
+ printf("[BEGIN] thread-local-cleanup\n");
+
+ for (int i=0; i < 1000; ++i) {
+ void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST);
+ if ( handle == NULL ) {
+ printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
+ return 0;
+ }
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
+ return 0;
+ }
+ }
+
+ printf("[PASS] thread-local-cleanup\n");
+
+ return 0;
+}
+
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);
+ _dyld_get_shared_cache_uuid(curUuid);
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);
+ //printf(" cur: 0x%09llX -> 0x%09llX off=0x%0llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->textSegmentOffset, info->path);
});
if ( result != 0 ) {
FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed");