+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-
-
-my $sdk = $ENV{"SDKROOT"};
-my $availCmd = $sdk . "/usr/local/libexec/availability.pl";
-
-sub expandVersions
-{
- my $macroPrefix = shift;
- my $availArg = shift;
-
- my $cmd = $availCmd . " " . $availArg;
- my $versionList = `$cmd`;
- my $tmp = $versionList;
- while ($tmp =~ m/^\s*([\S]+)(.*)$/) {
- my $vers = $1;
- $tmp = $2;
-
- my $major = 0;
- my $minor = 0;
- my $revision = 0;
- my $uvers;
-
- if ($vers =~ m/^(\d+)$/) {
- $major = $1;
- $uvers = sprintf("%d_0", $major);
- } elsif ($vers =~ m/^(\d+).(\d+)$/) {
- $major = $1;
- $minor = $2;
- $uvers = sprintf("%d_%d", $major, $minor);
- } elsif ($vers =~ m/^(\d+).(\d+).(\d+)$/) {
- $major = $1;
- $minor = $2;
- $revision = $3;
- if ($revision == 0) {
- $uvers = sprintf("%d_%d", $major, $minor);
- }
- else {
- $uvers = sprintf("%d_%d_%d", $major, $minor, $revision);
- }
- }
- printf "#define %s%-18s 0x00%02X%02X%02X\n", $macroPrefix, $uvers, $major, $minor, $revision;
- }
-}
-
-
-
-
-while(<STDIN>)
-{
- if(m/^\/\/\@MAC_VERSION_DEFS\@$/) {
- expandVersions("DYLD_MACOSX_VERSION_", "--macosx");
- }
- elsif(m/^\/\/\@IOS_VERSION_DEFS\@$/) {
- expandVersions("DYLD_IOS_VERSION_", "--ios");
- }
- elsif(m/^\/\/\@WATCHOS_VERSION_DEFS\@$/) {
- expandVersions("DYLD_WATCHOS_VERSION_", "--watchos");
- }
- else {
- print $_;
- }
-}
-
--- /dev/null
+#!/usr/bin/env ruby
+
+require 'yaml'
+
+$availCmd = ENV["SDKROOT"] + "/usr/local/libexec/availability.pl";
+
+def versionString(vers)
+ uvers = ""
+ if vers =~ /^(\d+)$/
+ uvers = "#{$1}_0"
+ elsif vers =~ /^(\d+).(\d+)$/
+ uvers = "#{$1}_#{$2}"
+ elsif vers =~ /^(\d+).(\d+).(\d+)$/
+ if $3 == 0
+ uvers = "#{$1}_#{$2}"
+ else
+ uvers = "#{$1}_#{$2}_#{$3}"
+ end
+ end
+ uvers
+end
+
+def versionHex(vers)
+ major = 0;
+ minor = 0;
+ revision = 0;
+
+ if vers =~ /^(\d+)$/
+ major = $1.to_i;
+ elsif vers =~ /^(\d+).(\d+)$/
+ major = $1.to_i;
+ minor = $2.to_i;
+ elsif vers =~ /^(\d+).(\d+).(\d+)$/
+ major = $1.to_i;
+ minor = $2.to_i;
+ revision = $3.to_i;
+ end
+ "0x00#{major.to_s(16).rjust(2, '0')}#{minor.to_s(16).rjust(2, '0')}#{revision.to_s(16).rjust(2, '0')}"
+end
+
+def expandVersions(prefix, arg)
+ versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
+ versionList.each { |version|
+ puts "#define #{prefix}#{versionString(version)}".ljust(48, ' ') + versionHex(version)
+ }
+end
+
+def expandPlatformVersions(prefix, platform, arg)
+ versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
+ versionList.each { |version|
+ puts "static dyld_build_version_t dyld_platform_version_#{prefix}_#{versionString(version)}".ljust(72, ' ') + "= { .platform = #{platform}, .version = #{versionHex(version)} };"
+ }
+end
+
+def versionSetsForOSes(versionSets, key, platform, target)
+ puts "#if #{target}"
+ versionSets.each { |k,v|
+ puts "#define dyld_#{k}_os_versions dyld_platform_version_#{platform}_#{versionString(v[key].to_s)}"
+ }
+ puts "#endif /* #{target} */"
+end
+
+def expandVersionSets()
+ versionSets = YAML.load(`#{$availCmd} --sets`)
+ versionSetsForOSes(versionSets, "macos", "macOS", "TARGET_OS_OSX")
+ versionSetsForOSes(versionSets, "ios", "iOS", "TARGET_OS_IOS")
+ versionSetsForOSes(versionSets, "tvos", "tvOS", "TARGET_OS_TV")
+ versionSetsForOSes(versionSets, "watchos", "watchOS", "TARGET_OS_WATCH")
+ versionSetsForOSes(versionSets, "bridgeos", "bridgeOS", "TARGET_OS_BRIDGE")
+end
+
+ARGF.each do |line|
+ if line =~ /^\/\/\@MAC_VERSION_DEFS\@$/
+ expandVersions("DYLD_MACOSX_VERSION_", "--macosx")
+ elsif line =~ /^\/\/\@IOS_VERSION_DEFS\@$/
+ expandVersions("DYLD_IOS_VERSION_", "--ios")
+ elsif line =~ /^\/\/\@WATCHOS_VERSION_DEFS\@$/
+ expandVersions("DYLD_WATCHOS_VERSION_", "--watchos")
+ elsif line =~ /^\/\/\@MACOS_PLATFORM_VERSION_DEFS\@$/
+ expandPlatformVersions("macOS", "PLATFORM_MACOS", "--macosx")
+ elsif line =~ /^\/\/\@IOS_PLATFORM_VERSION_DEFS\@$/
+ expandPlatformVersions("iOS", "PLATFORM_IOS", "--ios")
+ elsif line =~ /^\/\/\@WATCHOS_PLATFORM_VERSION_DEFS\@$/
+ expandPlatformVersions("watchOS", "PLATFORM_WATCHOS", "--watchos")
+ elsif line =~ /^\/\/\@TVOS_PLATFORM_VERSION_DEFS\@$/
+ expandPlatformVersions("tvOS", "PLATFORM_TVOS", "--appletvos")
+ elsif line =~ /^\/\/\@BRIDGEOS_PLATFORM_VERSION_DEFS\@$/
+ expandPlatformVersions("bridgeOS", "PLATFORM_BRIDGEOS", "--bridgeos")
+ elsif line =~ /^\/\/\@VERSION_SET_DEFS\@$/
+ expandVersionSets()
+ else
+ puts line
+ end
+end
INSTALL_PATH = /usr/lib
//:configuration = Debug
-GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 DEBUG=1
+GCC_PREPROCESSOR_DEFINITIONS = 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
+GCC_PREPROCESSOR_DEFINITIONS = 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 -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
+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-lcompiler_rt
+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-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-lcompiler_rt
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
+IS_ZIPPERED = YES
-
-CODE_SIGN_ENTITLEMENTS = dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist
-
#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
+
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1
--- /dev/null
+0x1f070000 dyld.static_intializer
+0x1f070004 dyld.launch_executable
+0x1f070008 dyld.map_file
+0x1f07000c dyld.apply_fixups
+0x1f070010 dyld.attach_codesignature
+0x1f070014 dyld.build_closure
+0x1f070018 dyld.add_image_callback
+0x1f07001c dyld.remove_image_callback
+0x1f070020 dyld.objc_image_init
+0x1f070024 dyld.objc_images_map
+0x1f070028 dyld.apply_interposing
+0x1f07002c dyld.gdb_image_notifier
+0x1f070030 dyld.remote_image_notifier
+0x1f080000 dyld.dlopen
+0x1f080004 dyld.dlopen_preflight
+0x1f080008 dyld.dlclose
+0x1f08000c dyld.dlsym
+0x1f080010 dyld.dladdr
+0x1f090000 dyld.DEBUG.vm_remap
+0x1f090004 dyld.DEBUG.vm_dealloc
+0x1f090008 dyld.DEBUG.map_loop
+0x1f09000c dyld.DEBUG.marker
--- /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">
+<array>
+ <dict>
+ <key>Name</key>
+ <string>dyld</string>
+ <key>Children</key>
+ <array>
+ <dict>
+ <key>Name</key>
+ <string>dlopen</string>
+ <key>Type</key>
+ <string>Interval</string>
+ <key>EventsMatchedBy</key>
+ <string>Arg1</string>
+ <key>KTraceCodeBegin</key>
+ <string>0x1f070005</string>
+ <key>KTraceCodeEnd</key>
+ <string>0x1f070006</string>
+ <key>ArgValueTypesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>String</string>
+ </dict>
+ <key>ArgNamesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Path</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Name</key>
+ <string>dlopen_preflight</string>
+ <key>Type</key>
+ <string>Interval</string>
+ <key>EventsMatchedBy</key>
+ <string>Arg1</string>
+ <key>KTraceCodeBegin</key>
+ <string>0x1f070009</string>
+ <key>KTraceCodeEnd</key>
+ <string>0x1f07000a</string>
+ <key>ArgValueTypesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>String</string>
+ </dict>
+ <key>ArgNamesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Path</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Name</key>
+ <string>map_file</string>
+ <key>Type</key>
+ <string>Interval</string>
+ <key>EventsMatchedBy</key>
+ <string>Arg1</string>
+ <key>KTraceCodeBegin</key>
+ <string>0x1f070005</string>
+ <key>KTraceCodeEnd</key>
+ <string>0x1f070006</string>
+ <key>ArgValueTypesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>String</string>
+ </dict>
+ <key>ArgNamesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Path</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Name</key>
+ <string>dlsym</string>
+ <key>Type</key>
+ <string>Interval</string>
+ <key>EventsMatchedBy</key>
+ <string>Arg1</string>
+ <key>KTraceCodeBegin</key>
+ <string>0x1f07000d</string>
+ <key>KTraceCodeEnd</key>
+ <string>0x1f07000e</string>
+ <key>ArgValueTypesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>String</string>
+ </dict>
+ <key>ArgNamesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Path</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Name</key>
+ <string>Static Initializer</string>
+ <key>Type</key>
+ <string>Interval</string>
+ <key>EventsMatchedBy</key>
+ <string>Arg1</string>
+ <key>KTraceCodeBegin</key>
+ <string>0x1f070001</string>
+ <key>KTraceCodeEnd</key>
+ <string>0x1f070002</string>
+ <key>ArgValueTypesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Hex</string>
+ <key>Arg3</key>
+ <string>Hex</string>
+ </dict>
+ <key>ArgNamesBegin</key>
+ <dict>
+ <key>Arg2</key>
+ <string>Mach Header</string>
+ <key>Arg3</key>
+ <string>Initializer Address</string>
+ </dict>
+ </dict>
+ </array>
+ </dict>
+</array>
+</plist>
F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */,
F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */,
F94182DC1E60F16900D8EF25 /* PBXTargetDependency */,
+ C187B90C1FE067590042D3B7 /* PBXTargetDependency */,
);
name = update_dyld_shared_cache;
productName = update_dyld_shared_cache;
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 */; };
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 */; };
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 */; };
+ 37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */ = {isa = PBXBuildFile; fileRef = 37918AC42058913800F39A77 /* dyld.codes */; };
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 */; };
+ 37F597D52061ED0B00F9B6F9 /* dyld_usage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */; };
+ 37F597D72061ED3200F9B6F9 /* libktrace.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F597D62061ED3200F9B6F9 /* libktrace.tbd */; };
+ C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */; };
+ C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+ C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+ C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+ C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+ C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+ C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+ C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+ C1960ED12090D9F6007E3E6B /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ C1960ED22090D9FA007E3E6B /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ C1960ED32090D9FF007E3E6B /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */; };
+ C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C1D268371FE0BC5F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C1D268391FE0BC94009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C1D2683A1FE0BCF3009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.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 */; };
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 */; };
+ F92756811F68AF4D000820EE /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F92756821F68AF4D000820EE /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F92756831F68AF4D000820EE /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F92756841F68AF4D000820EE /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F92756851F68AF4D000820EE /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F92756861F68AF4D000820EE /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
+ F936BF9720323F0F00568B23 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+ F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+ F93D73481F8FF780007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ F93F46521FA420850060D9F9 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
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"; }; };
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 */; };
+ F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F9653F901FAE51C9008B5D93 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F9653F911FAE51C9008B5D93 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+ F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.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, ); }; };
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 */; };
+ F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.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 */; };
+ F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=hidden"; }; };
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 */; };
+ F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+ F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
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 */; };
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 */; };
+ F9DFEA6C1F50DD16003BF8A7 /* Closure.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA6B1F50DD16003BF8A7 /* Closure.h */; };
+ F9DFEA701F50FDE5003BF8A7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F9DFEA721F54BD83003BF8A7 /* ClosureWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */; };
+ F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */; };
+ F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F9DFEA791F55DDC0003BF8A7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+ F9DFEA7A1F55DDC4003BF8A7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+ F9DFEA7B1F55DDC7003BF8A7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+ F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.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 */; };
F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */; };
F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */; };
F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */; };
- F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; };
+ F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; settings = {COMPILER_FLAGS = "-fno-builtin"; }; };
F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */; };
F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; };
F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; };
remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
remoteInfo = update_dyld_shared_cache;
};
+ C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = C187B8FF1FE063A40042D3B7;
+ remoteInfo = libslc_builder;
+ };
D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
remoteInfo = libdyld.dylib;
};
- F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = F9AB02B71F329FAA00EE96C4;
- remoteInfo = libclosured;
- };
- F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = F922C8161F33B73800D8F479;
- remoteInfo = "libclosured-stub";
- };
F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
);
runOnlyForDeploymentPostprocessing = 1;
};
+ 37918AC32058912100F39A77 /* install ktrace codes file */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/local/share/misc;
+ dstSubfolderSpec = 0;
+ files = (
+ 37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */,
+ );
+ name = "install ktrace codes file";
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ 37F597CB2061EB4200F9B6F9 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+ C187B9041FE063A40042D3B7 /* usr|local|include|mach-o */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include";
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ 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;
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>"; };
+ 37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = "<group>"; };
+ 37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; 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>"; };
+ 37F597CD2061EB4200F9B6F9 /* dyld_usage */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_usage; sourceTree = BUILT_PRODUCTS_DIR; };
+ 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_usage.cpp; path = src/dyld_usage.cpp; sourceTree = "<group>"; };
+ 37F597D62061ED3200F9B6F9 /* libktrace.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libktrace.tbd; path = usr/lib/libktrace.tbd; sourceTree = SDKROOT; };
37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; };
+ C187B90A1FE063A40042D3B7 /* slc_builder.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = slc_builder.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ C18A75F5209940A500DC01BB /* JSONWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONWriter.h; path = dyld3/JSONWriter.h; sourceTree = "<group>"; };
+ C19D50142087E4BC00563DAF /* SupportedArchs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SupportedArchs.h; path = dyld3/SupportedArchs.h; sourceTree = "<group>"; };
+ C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = mrm_shared_cache_builder.cpp; path = "dyld3/shared-cache/mrm_shared_cache_builder.cpp"; sourceTree = "<group>"; };
+ C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = mrm_shared_cache_builder.h; path = "dyld3/shared-cache/mrm_shared_cache_builder.h"; sourceTree = "<group>"; };
+ C1D268321FE09843009F115B /* ClosureFileSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureFileSystem.h; path = dyld3/ClosureFileSystem.h; sourceTree = "<group>"; };
+ C1D268331FE0A21F009F115B /* ClosureFileSystemPhysical.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureFileSystemPhysical.h; path = dyld3/ClosureFileSystemPhysical.h; sourceTree = "<group>"; };
+ C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureFileSystemPhysical.cpp; path = dyld3/ClosureFileSystemPhysical.cpp; sourceTree = "<group>"; };
EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; };
EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; };
EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; };
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; };
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>"; };
+ F92756871F7098FB000820EE /* Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Array.h; path = dyld3/Array.h; sourceTree = "<group>"; };
+ F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = "<group>"; usesTabs = 1; };
+ F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; usesTabs = 1; };
F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = "<group>"; };
F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = "<group>"; };
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>"; };
+ F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; 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>"; };
- F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; };
+ F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = "<group>"; usesTabs = 1; };
+ F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = "<group>"; usesTabs = 1; };
+ F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; usesTabs = 1; };
+ F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; usesTabs = 1; };
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>"; };
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>"; };
+ F96D19711D7F63EE007AF3CE /* expand.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = expand.rb; path = bin/expand.rb; 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>"; };
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>"; };
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>"; };
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; };
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>"; };
- 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>"; };
F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = "<group>"; };
F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = "<group>"; };
F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
+ F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOLoaded.cpp; path = dyld3/MachOLoaded.cpp; sourceTree = "<group>"; };
+ F9A5E6161F5C967C0030C490 /* MachOLoaded.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOLoaded.h; path = dyld3/MachOLoaded.h; sourceTree = "<group>"; };
+ F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAnalyzer.cpp; path = dyld3/MachOAnalyzer.cpp; sourceTree = "<group>"; };
+ F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOFile.cpp; path = dyld3/MachOFile.cpp; sourceTree = "<group>"; };
+ F9A5E61A1F5F1BFA0030C490 /* MachOFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOFile.h; path = dyld3/MachOFile.h; sourceTree = "<group>"; };
+ F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOAnalyzer.h; path = dyld3/MachOAnalyzer.h; 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; };
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; };
+ F9DFEA6B1F50DD16003BF8A7 /* Closure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Closure.h; path = dyld3/Closure.h; sourceTree = "<group>"; };
+ F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Closure.cpp; path = dyld3/Closure.cpp; sourceTree = "<group>"; };
+ F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClosureWriter.h; path = dyld3/ClosureWriter.h; sourceTree = "<group>"; };
+ F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureWriter.cpp; path = dyld3/ClosureWriter.cpp; sourceTree = "<group>"; };
+ F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureBuilder.h; path = dyld3/ClosureBuilder.h; sourceTree = "<group>"; };
+ F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureBuilder.cpp; path = dyld3/ClosureBuilder.cpp; sourceTree = "<group>"; };
+ F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosurePrinter.cpp; path = dyld3/ClosurePrinter.cpp; sourceTree = "<group>"; };
+ F9DFEA7E1F588558003BF8A7 /* ClosurePrinter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosurePrinter.h; path = dyld3/ClosurePrinter.h; sourceTree = "<group>"; };
F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; 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; };
+ F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; usesTabs = 0; };
F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; };
- F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; };
+ F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyldExceptions.c; path = src/dyldExceptions.c; sourceTree = SOURCE_ROOT; };
F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldInitialization.cpp; path = src/dyldInitialization.cpp; sourceTree = SOURCE_ROOT; };
F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldLock.cpp; path = src/dyldLock.cpp; sourceTree = SOURCE_ROOT; };
F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldNew.cpp; path = src/dyldNew.cpp; sourceTree = SOURCE_ROOT; };
F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = dyldStartup.s; path = src/dyldStartup.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; };
F9ED4CD00630A7F100DF4E74 /* glue.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = glue.c; path = src/glue.c; sourceTree = SOURCE_ROOT; };
- F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; };
- F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; };
- F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; };
- F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; };
+ F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+ F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+ F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+ F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; usesTabs = 1; };
F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = stub_binding_helper.s; path = src/stub_binding_helper.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; };
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>"; };
);
runOnlyForDeploymentPostprocessing = 0;
};
- F922C8141F33B73800D8F479 /* Frameworks */ = {
+ 37F597CA2061EB4200F9B6F9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 37F597D72061ED3200F9B6F9 /* libktrace.tbd in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ C187B9031FE063A40042D3B7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
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;
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 37918ABF2058908000F39A77 /* tracing */ = {
+ isa = PBXGroup;
+ children = (
+ 37918AC0205890D700F39A77 /* dyld.plist */,
+ 37918AC42058913800F39A77 /* dyld.codes */,
+ );
+ name = tracing;
+ path = doc/tracing;
+ sourceTree = SOURCE_ROOT;
+ };
EF799FE7070D27BB00F78484 /* man */ = {
isa = PBXGroup;
children = (
F94C22231E513CA90079E5DD /* Frameworks */ = {
isa = PBXGroup;
children = (
- F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */,
+ 37F597D62061ED3200F9B6F9 /* libktrace.tbd */,
37F7A5961BB363820039043A /* Bom.framework */,
376ED1D71C46F2710051DD54 /* Metabom.framework */,
F94C22241E513CA90079E5DD /* CoreFoundation.framework */,
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 */,
+ F96D19A71D9363D6007AF3CE /* AllImages.h */,
+ F96D19A61D9363D6007AF3CE /* AllImages.cpp */,
+ F96D19C11D95C6D6007AF3CE /* APIs.h */,
+ F96D19A51D9363D6007AF3CE /* APIs.cpp */,
+ F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */,
+ F92756871F7098FB000820EE /* Array.h */,
+ F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */,
+ F9DFEA6B1F50DD16003BF8A7 /* Closure.h */,
+ F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */,
+ F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */,
+ F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */,
+ C1D268321FE09843009F115B /* ClosureFileSystem.h */,
+ C1D268331FE0A21F009F115B /* ClosureFileSystemPhysical.h */,
+ C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */,
+ F9DFEA7E1F588558003BF8A7 /* ClosurePrinter.h */,
+ F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */,
+ F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */,
+ F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */,
+ F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */,
+ F98692001DC3EF4800CBEDE6 /* Diagnostics.h */,
+ C18A75F5209940A500DC01BB /* JSONWriter.h */,
+ F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */,
+ F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */,
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 */,
+ F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */,
+ F9A5E61A1F5F1BFA0030C490 /* MachOFile.h */,
+ F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */,
+ F9A5E6161F5C967C0030C490 /* MachOLoaded.h */,
+ F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */,
+ F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.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 */,
+ F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */,
+ F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */,
+ 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */,
+ 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */,
+ C19D50142087E4BC00563DAF /* SupportedArchs.h */,
);
name = dyld3;
sourceTree = "<group>";
F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */,
- F9EDC0A01F0481B400B030F4 /* closured.xcconfig */,
);
path = configs;
sourceTree = SOURCE_ROOT;
};
- F97FF3571C23638F000ACDD2 /* nocr */ = {
- isa = PBXGroup;
- children = (
- F97FF3581C23638F000ACDD2 /* main.c */,
- );
- path = nocr;
- sourceTree = "<group>";
- };
F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
isa = PBXGroup;
children = (
F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
- F963546A1DD8D8D300895049 /* ImageProxy.h */,
- F963546B1DD8F2A800895049 /* ImageProxy.cpp */,
37908A2C1E3A85A4009613FA /* Manifest.h */,
37908A281E3A853E009613FA /* Manifest.mm */,
F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
+ C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */,
+ C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */,
);
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 */,
F9ED4CC30630A7BE00DF4E74 /* doc */,
F9ED4CBE0630A7B100DF4E74 /* include */,
- F97FF3571C23638F000ACDD2 /* nocr */,
F9ED4C990630A76000DF4E74 /* Products */,
F96D19A41D9363B7007AF3CE /* dyld3 */,
F939373D0A94FC4700070A07 /* launch-cache */,
F97FF3561C23638F000ACDD2 /* nocr */,
F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
- F9DDEDB21E2878CA00A753DC /* closured */,
- F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */,
- F922C8171F33B73800D8F479 /* libclosured.dylib */,
+ C187B90A1FE063A40042D3B7 /* slc_builder.dylib */,
+ 37F597CD2061EB4200F9B6F9 /* dyld_usage */,
);
name = Products;
sourceTree = "<group>";
F9ED4CBB0630A7AA00DF4E74 /* src */ = {
isa = PBXGroup;
children = (
+ F93F46511FA420630060D9F9 /* execserver.defs */,
F97FF35F1C236402000ACDD2 /* nocr.c */,
+ 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */,
F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
F9ED4CC80630A7F100DF4E74 /* dyld.h */,
F9ED4CBE0630A7B100DF4E74 /* include */ = {
isa = PBXGroup;
children = (
- F96D19711D7F63EE007AF3CE /* expand.pl */,
+ F96D19711D7F63EE007AF3CE /* expand.rb */,
F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
F98D274C0AA79D7400416316 /* dyld_images.h */,
F918691408B16D2500E0F9DB /* dyld-interposing.h */,
F9ED4CC30630A7BE00DF4E74 /* doc */ = {
isa = PBXGroup;
children = (
+ 37918ABF2058908000F39A77 /* tracing */,
EF799FE7070D27BB00F78484 /* man */,
);
name = doc;
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
- F922C8151F33B73800D8F479 /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
F98F1FBB1E4029CA00EF868D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
+ F9DFEA6C1F50DD16003BF8A7 /* Closure.h in Headers */,
F99006DD1E411BA70013456D /* dyld_images.h in Headers */,
F99006DE1E411BBC0013456D /* dyld.h in Headers */,
F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */,
+ F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */,
F960A78A1E40569400840176 /* dyld-interposing.h in Headers */,
+ F9DFEA721F54BD83003BF8A7 /* ClosureWriter.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 */
productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
productType = "com.apple.product-type.tool";
};
- F922C8161F33B73800D8F479 /* libclosured-stub */ = {
+ 37F597CC2061EB4200F9B6F9 /* dyld_usage */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 37F597D32061EB4200F9B6F9 /* Build configuration list for PBXNativeTarget "dyld_usage" */;
+ buildPhases = (
+ 37F597C92061EB4200F9B6F9 /* Sources */,
+ 37F597CA2061EB4200F9B6F9 /* Frameworks */,
+ 37F597CB2061EB4200F9B6F9 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dyld_usage;
+ productName = dyld_usage;
+ productReference = 37F597CD2061EB4200F9B6F9 /* dyld_usage */;
+ productType = "com.apple.product-type.tool";
+ };
+ C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */ = {
isa = PBXNativeTarget;
- buildConfigurationList = F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */;
+ buildConfigurationList = C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */;
buildPhases = (
- F922C8131F33B73800D8F479 /* Sources */,
- F922C8141F33B73800D8F479 /* Frameworks */,
- F922C8151F33B73800D8F479 /* Headers */,
+ C187B9001FE063A40042D3B7 /* Sources */,
+ C187B9031FE063A40042D3B7 /* Frameworks */,
+ C187B9041FE063A40042D3B7 /* usr|local|include|mach-o */,
);
buildRules = (
);
dependencies = (
);
- name = "libclosured-stub";
- productName = "libclosured-stub";
- productReference = F922C8171F33B73800D8F479 /* libclosured.dylib */;
+ name = libslc_builder.dylib;
+ productName = dsc;
+ productReference = C187B90A1FE063A40042D3B7 /* slc_builder.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
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" */;
F907E2490FA6469000BFEDBD /* install iPhone file */,
F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */,
F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */,
+ 371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */,
);
buildRules = (
F921D318070376B0000D1056 /* PBXBuildRule */,
F921D3160703769A000D1056 /* PBXBuildRule */,
);
dependencies = (
- F922C8121F33B62700D8F479 /* PBXTargetDependency */,
F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
F96543A11E343601003C5540 /* PBXTargetDependency */,
);
F98F1FBB1E4029CA00EF868D /* Headers */,
F960A78C1E405E2300840176 /* expand dyld_priv.h macros */,
F99006DF1E411C500013456D /* install dlfcn.h */,
+ 37918AC32058912100F39A77 /* install ktrace codes file */,
);
buildRules = (
F921D31E070376F1000D1056 /* PBXBuildRule */,
F9574C4906C94DA700142BFA /* PBXBuildRule */,
);
dependencies = (
- F922C81E1F33B96300D8F479 /* PBXTargetDependency */,
);
name = libdyld.dylib;
productName = libdyld;
F9ED4C8B0630A72300DF4E74 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0900;
+ LastUpgradeCheck = 1000;
TargetAttributes = {
37A0AD0A1C15FFF500731E50 = {
CreatedOnToolsVersion = 8.0;
};
- F922C8161F33B73800D8F479 = {
- CreatedOnToolsVersion = 9.0;
+ 37F597CC2061EB4200F9B6F9 = {
+ CreatedOnToolsVersion = 10.0;
+ ProvisioningStyle = Automatic;
};
F97C61A61DBAD1A900A84CD7 = {
CreatedOnToolsVersion = 8.0;
F97FF3551C23638F000ACDD2 = {
CreatedOnToolsVersion = 8.0;
};
- F9AB02B71F329FAA00EE96C4 = {
- CreatedOnToolsVersion = 9.0;
- };
- F9DDEDB11E2878CA00A753DC = {
- CreatedOnToolsVersion = 8.2;
- DevelopmentTeam = 59GAB85EFG;
- ProvisioningStyle = Automatic;
- };
F9F6F4271C1FB0A700BD8FED = {
CreatedOnToolsVersion = 8.0;
};
37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
F908134211D3ED0B00626CC1 /* libdyld */,
F9ED4C970630A76000DF4E74 /* dyld */,
- F9DDEDB11E2878CA00A753DC /* closured */,
F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */,
F9F2A5580F7AEE9800B7C9EB /* libdsc */,
F9D1001114D8D0BA00099D91 /* dsc_extractor */,
+ C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */,
F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
F97FF3551C23638F000ACDD2 /* nocr */,
- F9AB02B71F329FAA00EE96C4 /* libclosured */,
- F922C8161F33B73800D8F479 /* libclosured-stub */,
+ 37F597CC2061EB4200F9B6F9 /* dyld_usage */,
);
};
/* End PBXProject section */
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
- shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ 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[ \\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_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ showEnvVarsInLog = 0;
+ };
+ 371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Suppress simulator dyld_usage";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "# dyld_usage requires libktrace which is not available in the simualtor\n# The target builds a dummy app that we delete\nif [ \"${PRODUCT_NAME}\" != \"dyld_sim\" ]\nthen\nxcodebuild install -target dyld_usage SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi";
showEnvVarsInLog = 0;
};
377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = {
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
- shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ 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[ \\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_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
showEnvVarsInLog = 0;
};
F907E2490FA6469000BFEDBD /* install iPhone file */ = {
);
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;
- };
- 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";
+ 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[ \\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_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\n";
showEnvVarsInLog = 0;
};
F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
);
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";
+ 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\tif [ \"${RC_BRIDGE}\" != \"YES\" ]\n\tthen\n\t\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}\"\n\tfi\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;
};
F959621018849DF20003E4D4 /* add dyld symlink */ = {
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/bash;
- shellScript = "if [[ \"${PLATFORM_NAME}\" == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
+ shellScript = "if [[ \"${PLATFORM_NAME}\" == *simulator* ]]\nthen\n\tcd ${DSTROOT}\n mkdir -p usr/lib/system/\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
showEnvVarsInLog = 0;
};
F960A78C1E405E2300840176 /* expand dyld_priv.h macros */ = {
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n";
+ shellScript = "${SRCROOT}/bin/expand.rb < ${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 */ = {
);
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";
+ 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[ \\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_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\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 */ = {
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- 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";
+ shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.rb < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n";
showEnvVarsInLog = 0;
};
F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = {
);
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";
+ 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\nrm -rf ${DSTROOT}/usr/local/bin/dyld_shared_cache_util\nrm -rf ${DSTROOT}/usr/local/bin/dyld_closure_util\nfi\n";
showEnvVarsInLog = 0;
};
F9D050C811DD701A00FB0A29 /* configure archives */ = {
);
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";
+ 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# link with crypto archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libcorecrypto_static.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libcorecrypto_static.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt\nfi\n";
showEnvVarsInLog = 0;
};
F9F6F42B1C1FB0AE00BD8FED /* build */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+ F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */,
+ F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */,
+ F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */,
+ F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */,
+ C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */,
+ F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */,
+ F93D73531F8FF7C2007D9413 /* MachOAnalyzer.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 */,
+ C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+ F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */,
+ F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */,
+ F93D734D1F8FF79E007D9413 /* MachOAnalyzer.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 */,
+ F93D73481F8FF780007D9413 /* Closure.cpp in Sources */,
+ F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */,
+ F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */,
+ C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- F922C8131F33B73800D8F479 /* Sources */ = {
+ 37F597C92061EB4200F9B6F9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */,
+ 37F597D52061ED0B00F9B6F9 /* dyld_usage.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ C187B9001FE063A40042D3B7 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */,
+ C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */,
+ C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */,
+ C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */,
+ C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */,
+ C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */,
+ C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */,
+ C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */,
+ C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */,
+ C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */,
+ C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */,
+ C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */,
+ C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */,
+ C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */,
+ C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */,
+ C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */,
+ C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */,
+ F92756811F68AF4D000820EE /* Closure.cpp in Sources */,
+ F92756821F68AF4D000820EE /* ClosureWriter.cpp in Sources */,
+ C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+ F92756831F68AF4D000820EE /* ClosureBuilder.cpp in Sources */,
+ F92756841F68AF4D000820EE /* MachOFile.cpp in Sources */,
+ F92756851F68AF4D000820EE /* MachOLoaded.cpp in Sources */,
+ F92756861F68AF4D000820EE /* MachOAnalyzer.cpp 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;
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */,
+ F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */,
+ F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */,
+ C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */,
+ F9653F901FAE51C9008B5D93 /* ClosureWriter.cpp in Sources */,
+ F9653F911FAE51C9008B5D93 /* MachOFile.cpp in Sources */,
+ F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */,
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;
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 */,
+ C1D268371FE0BC5F009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+ F9DFEA791F55DDC0003BF8A7 /* Closure.cpp in Sources */,
+ F9DFEA7A1F55DDC4003BF8A7 /* ClosureWriter.cpp in Sources */,
+ F9DFEA7B1F55DDC7003BF8A7 /* ClosureBuilder.cpp in Sources */,
+ F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */,
+ F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */,
+ F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */,
+ F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ F93F46521FA420850060D9F9 /* execserver.defs in Sources */,
F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
- F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */,
+ C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */,
+ C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */,
+ C1960ED32090D9FF007E3E6B /* MachOFile.cpp in Sources */,
+ C1960ED22090D9FA007E3E6B /* MachOAnalyzer.cpp in Sources */,
F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */,
+ C1960ED12090D9F6007E3E6B /* MachOLoaded.cpp in Sources */,
F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */,
);
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 */,
+ C1D2683A1FE0BCF3009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */,
F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */,
F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */,
+ 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */,
F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */,
F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */,
F9ED4CE10630A7F100DF4E74 /* ImageLoader.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 */,
+ F936BF9720323F0F00568B23 /* FileUtils.cpp in Sources */,
+ F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */,
+ F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */,
+ F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */,
+ F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */,
+ F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */,
+ F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */,
+ F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
files = (
F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */,
37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */,
+ F9DFEA701F50FDE5003BF8A7 /* Closure.cpp in Sources */,
F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
+ C1D268391FE0BC94009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */,
F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */,
F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */,
F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */,
F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */,
F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */,
- 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 */,
F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */,
F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */,
F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */,
+ F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */,
+ F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */,
+ F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */,
F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */,
- F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */,
- F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */,
- F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */,
+ F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */,
+ F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */,
F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
};
+ C187B90C1FE067590042D3B7 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */;
+ targetProxy = C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */;
+ };
D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
};
- F922C8121F33B62700D8F479 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = F9AB02B71F329FAA00EE96C4 /* libclosured */;
- targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */;
- };
- F922C81E1F33B96300D8F479 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = F922C8161F33B73800D8F479 /* libclosured-stub */;
- targetProxy = F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */;
- };
F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
- OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ OTHER_CFLAGS = (
+ "-DBOM_SUPPORT=1",
+ "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+ );
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
SUPPORTED_PLATFORMS = macosx;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
- OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ OTHER_CFLAGS = (
+ "-DBOM_SUPPORT=1",
+ "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+ );
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
SUPPORTED_PLATFORMS = macosx;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
- OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ OTHER_CFLAGS = (
+ "-DBOM_SUPPORT=1",
+ "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+ );
PRODUCT_NAME = multi_dyld_shared_cache_builder;
SDKROOT = macosx.internal;
STRIP_INSTALLED_PRODUCT = NO;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
- OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ OTHER_CFLAGS = (
+ "-DBOM_SUPPORT=1",
+ "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+ );
PRODUCT_NAME = multi_dyld_shared_cache_builder;
SDKROOT = macosx.internal;
STRIP_INSTALLED_PRODUCT = NO;
};
name = Release;
};
- F908134311D3ED0C00626CC1 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_OPTIMIZATION_LEVEL = 0;
- INSTALLHDRS_COPY_PHASE = YES;
- PRODUCT_NAME = libdyld;
- };
- name = Debug;
- };
- F908134411D3ED0C00626CC1 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- INSTALLHDRS_COPY_PHASE = YES;
- PRODUCT_NAME = libdyld;
- ZERO_LINK = NO;
- };
- name = Release;
- };
- F922C8191F33B73800D8F479 /* Debug */ = {
+ 37F597D12061EB4200F9B6F9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_COMMA = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
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_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;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+ VALID_ARCHS = "arm64 arm64e x86_64";
};
name = Debug;
};
- F922C81A1F33B73800D8F479 /* Release */ = {
+ 37F597D22061EB4200F9B6F9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_COMMA = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "-";
+ CODE_SIGN_STYLE = Automatic;
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;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+ VALID_ARCHS = "arm64 arm64e x86_64";
+ };
+ name = Release;
+ };
+ C187B9081FE063A40042D3B7 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LIBRARY = "libc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_CPP_EXCEPTIONS = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ "BUILDING_CACHE_BUILDER=1",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_WARN_SIGN_COMPARE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
+ MACH_O_TYPE = mh_dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = slc_builder;
+ SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = NO;
+ STRIP_STYLE = "non-global";
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "x86_64 x86_64h";
+ };
+ name = Debug;
+ };
+ C187B9091FE063A40042D3B7 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LIBRARY = "libc++";
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+ "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+ );
+ GCC_ENABLE_CPP_EXCEPTIONS = YES;
+ GCC_ENABLE_CPP_RTTI = YES;
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_PEDANTIC = NO;
+ GCC_WARN_SHADOW = NO;
+ GCC_WARN_SIGN_COMPARE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALLHDRS_COPY_PHASE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
+ MACH_O_TYPE = mh_dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+ PRODUCT_NAME = slc_builder;
+ SDKROOT = macosx.internal;
+ STRIP_STYLE = "non-global";
+ USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+ VALID_ARCHS = "x86_64 x86_64h";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F908134311D3ED0C00626CC1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD)";
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALLHDRS_COPY_PHASE = YES;
+ PRODUCT_NAME = libdyld;
+ };
+ name = Debug;
+ };
+ F908134411D3ED0C00626CC1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD)";
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ INSTALLHDRS_COPY_PHASE = YES;
+ PRODUCT_NAME = libdyld;
+ ZERO_LINK = NO;
};
name = Release;
};
F93937350A94FB2900070A07 /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "BUILDING_CACHE_BUILDER=1",
+ "BUILDING_UPDATE_DYLD_CACHE_BUILDER=1",
+ "DEBUG=1",
+ );
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
"$(SRCROOT)/dyld3/shared-cache",
);
INSTALL_PATH = /usr/bin;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
+ CODE_SIGN_ENTITLEMENTS = "dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist";
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = s;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "BUILDING_CACHE_BUILDER=1",
+ "BUILDING_UPDATE_DYLD_CACHE_BUILDER=1",
+ );
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
"$(SRCROOT)/dyld3/shared-cache",
);
INSTALL_PATH = /usr/bin;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = "-stdlib=libc++";
PRODUCT_NAME = update_dyld_shared_cache;
};
F96354431DCD74A400895049 /* Debug */ = {
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;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
SDKROOT = macosx.internal;
SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
USE_HEADERMAP = NO;
- VALID_ARCHS = x86_64;
+ VALID_ARCHS = "x86_64 x86_64h";
};
name = Debug;
};
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;
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = s;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
GCC_THREADSAFE_STATICS = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
STRIP_INSTALLED_PRODUCT = YES;
STRIP_STYLE = debugging;
USE_HEADERMAP = NO;
- VALID_ARCHS = x86_64;
+ VALID_ARCHS = "x86_64 x86_64h";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
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;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
};
name = Debug;
};
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;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
};
name = Release;
};
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
PRODUCT_NAME = dyld_shared_cache_util;
SDKROOT = macosx.internal;
- SUPPORTED_PLATFORMS = macosx;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
};
name = Debug;
};
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++14";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_DYNAMIC_NO_PIC = NO;
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;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
};
name = Release;
};
baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
"$(ENTRY)",
);
STRIPFLAGS = "-S";
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
UNSTRIPPED_PRODUCT = NO;
VERSIONING_SYSTEM = "apple-generic";
WARNING_CFLAGS = (
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
"$(ALIGNMENT)",
"$(ENTRY)",
"-Wl,-no_data_const",
- "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__bss:__common",
+ "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__auth_ptr:__const:__crash_info:__data:__bss:__common",
);
STRIPFLAGS = "-S";
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
UNSTRIPPED_PRODUCT = NO;
VERSIONING_SYSTEM = "apple-generic";
WARNING_CFLAGS = (
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "BUILDING_LIBDYLD=1",
+ "DEBUG=1",
+ );
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
OTHER_CFLAGS = "";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
- "-Wglobal-constructors",
+ "-fno-exceptions",
);
OTHER_LDFLAGS = (
"-Wl,-no_inits",
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 ";
+ OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 -umbrella System";
PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
PRODUCT_NAME = dyld;
PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
SKIP_INSTALL = NO;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
SUPPORTS_TEXT_BASED_API = YES;
TAPI_VERIFY_MODE = Pedantic;
VERSIONING_SYSTEM = "apple-generic";
isa = XCBuildConfiguration;
baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_EMPTY_BODY = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDYLD=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
INSTALLHDRS_SCRIPT_PHASE = YES;
OTHER_CFLAGS = "";
OTHER_CPLUSPLUSFLAGS = (
- "-fno-exceptions",
"$(OTHER_CFLAGS)",
+ "-fno-exceptions",
);
OTHER_LDFLAGS = (
"-Wl,-no_inits",
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 ";
+ OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 -umbrella System";
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;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
SUPPORTS_TEXT_BASED_API = YES;
TAPI_VERIFY_MODE = Pedantic;
VERSIONING_SYSTEM = "apple-generic";
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "compiler-default";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
buildSettings = {
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
CLANG_CXX_LIBRARY = "compiler-default";
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
};
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;
- };
F9F2A55A0F7AEE9900B7C9EB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
};
name = Debug;
};
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
};
name = Release;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
+ 37F597D32061EB4200F9B6F9 /* Build configuration list for PBXNativeTarget "dyld_usage" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F908134311D3ED0C00626CC1 /* Debug */,
- F908134411D3ED0C00626CC1 /* Release */,
+ 37F597D12061EB4200F9B6F9 /* Debug */,
+ 37F597D22061EB4200F9B6F9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */ = {
+ C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- F922C8191F33B73800D8F479 /* Debug */,
- F922C81A1F33B73800D8F479 /* Release */,
+ C187B9081FE063A40042D3B7 /* Debug */,
+ C187B9091FE063A40042D3B7 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F908134311D3ED0C00626CC1 /* Debug */,
+ F908134411D3ED0C00626CC1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
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 = (
#include <string.h>
#include <stdint.h>
-#include <_simple.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <TargetConditionals.h>
#include <CommonCrypto/CommonDigest.h>
#include <dispatch/dispatch.h>
+#include <_simple.h>
+#include <array>
#include <algorithm>
#include "dlfcn.h"
+#include "dyld.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"
-
+#include "Closure.h"
+#include "MachOLoaded.h"
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
-
-extern "C" {
- #include "closuredProtocol.h"
-}
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
namespace dyld {
namespace dyld3 {
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
+pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+// forward declaration
+static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
+
+
uint32_t _dyld_image_count(void)
{
log_apis("_dyld_image_count()\n");
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;
+ return gAllImages.imageLoadAddressByIndex(imageIndex);
}
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();
+ const MachOLoaded* mf = (MachOLoaded*)mh;
+ if ( !mf->hasMachOMagic() )
+ return 0;
+
+ return mf->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);
+ const mach_header* mh = gAllImages.imageLoadAddressByIndex(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;
+ return gAllImages.imagePathByIndex(imageIndex);
}
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) {
+ gAllImages.mainExecutable()->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
if ( nameMatch(loadPath, libraryName) )
result = currentVersion;
});
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;
- }
+ __block int32_t result = -1;
+ gAllImages.forEachImage(^(const dyld3::LoadedImage& loadedImage, bool &stop) {
+ const char* installName;
+ uint32_t currentVersion;
+ uint32_t compatVersion;
+ if ( loadedImage.loadedAddress()->getDylibInstallName(&installName, &compatVersion, ¤tVersion) && nameMatch(installName, libraryName) ) {
+ result = currentVersion;
+ stop = true;
}
- }
- log_apis(" NSVersionOfRunTimeLibrary() => -1\n");
- return -1;
+ });
+ log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result);
+ return result;
}
-#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;
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
- MachOParser parser(gAllImages.mainExecutable());
- if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
- if ( platform == Platform::watchOS )
- return sdk;
- }
- return 0;
+ if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+ versionFound = true;
+ retval = sdk_version;
+ }
+ });
+
+ return retval;
}
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;
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
- 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;
+ if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+ versionFound = true;
+ retval = min_version;
+ }
+ });
+
+ return retval;
}
-#endif
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+ log_apis("dyld_get_program_sdk_bridge_os_version()\n");
-#if TARGET_OS_BRIDGE
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
-static uint32_t bridgeVersToIOSVers(uint32_t vers)
-{
- return vers + 0x00090000;
+ if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+ versionFound = true;
+ retval = sdk_version;
+ }
+ });
+
+ return retval;
}
-uint32_t dyld_get_program_sdk_bridge_os_version()
+uint32_t dyld_get_program_min_bridge_os_version()
+{
+ log_apis("dyld_get_program_min_bridge_os_version()\n");
+
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
+
+ if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+ versionFound = true;
+ retval = min_version;
+ }
+ });
+
+ return retval;
+ }
+
+//
+// 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_program_sdk_bridge_os_version()\n");
+ log_apis("dyld_get_sdk_version(%p)\n", mh);
+ __block bool versionFound = false;
+ __block uint32_t retval = 0;
+ dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
+
+ if (platform == ::dyld_get_active_platform()) {
+ versionFound = true;
+ switch (dyld3::dyld_get_base_platform(platform)) {
+ case PLATFORM_BRIDGEOS: retval = sdk_version + 0x00090000; return;
+ case PLATFORM_WATCHOS: retval = sdk_version + 0x00070000; return;
+ default: retval = sdk_version; return;
+ }
+ } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
+ //FIXME bringup hack
+ versionFound = true;
+ retval = 0x000C0000;
+ }
+ });
- Platform platform;
- uint32_t minOS;
- uint32_t sdk;
+ return retval;
+}
- MachOParser parser(gAllImages.mainExecutable());
- if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
- if ( platform == Platform::bridgeOS )
- return sdk;
+uint32_t dyld_get_program_sdk_version()
+{
+ log_apis("dyld_get_program_sdk_version()\n");
+ static uint32_t sProgramSDKVersion = 0;
+ if (sProgramSDKVersion == 0) {
+ sProgramSDKVersion = dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
}
- return 0;
+ return sProgramSDKVersion;
}
-uint32_t dyld_get_program_min_bridge_os_version()
+uint32_t dyld_get_min_os_version(const mach_header* mh)
{
- log_apis("dyld_get_program_min_bridge_os_version()\n");
+ log_apis("dyld_get_min_os_version(%p)\n", mh);
+ __block bool versionFound = false;
+ __block uint32_t retval = 0;
+ dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
+
+ if (platform == ::dyld_get_active_platform()) {
+ versionFound = true;
+ switch (dyld3::dyld_get_base_platform(platform)) {
+ case PLATFORM_BRIDGEOS: retval = min_version + 0x00090000; return;
+ case PLATFORM_WATCHOS: retval = min_version + 0x00070000; return;
+ default: retval = min_version; return;
+ }
+ } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
+ //FIXME bringup hack
+ versionFound = true;
+ retval = 0x000C0000;
+ }
+ });
- Platform platform;
- uint32_t minOS;
- uint32_t sdk;
+ return retval;
+}
- MachOParser parser(gAllImages.mainExecutable());
- if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
- if ( platform == Platform::bridgeOS )
- return minOS; // return raw minOS (not mapped to iOS version)
+dyld_platform_t dyld_get_active_platform(void) {
+ return gAllImages.platform();
+}
+
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
+ switch (platform) {
+ case PLATFORM_IOSMAC: return PLATFORM_IOS;
+ case PLATFORM_IOSSIMULATOR: return PLATFORM_IOS;
+ case PLATFORM_WATCHOSSIMULATOR: return PLATFORM_WATCHOS;
+ case PLATFORM_TVOSSIMULATOR: return PLATFORM_TVOS;
+ default: return platform;
}
- return 0;
}
-#endif
+bool dyld_is_simulator_platform(dyld_platform_t platform) {
+ switch(platform) {
+ case PLATFORM_IOSSIMULATOR:
+ case PLATFORM_WATCHOSSIMULATOR:
+ case PLATFORM_TVOSSIMULATOR:
+ return true;
+ default:
+ return false;
+ }
+}
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+ __block bool retval = false;
+ dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (dyld3::dyld_get_base_platform(platform) == version.platform && sdk_version >= version.version) {
+ retval = true;
+ }
+ });
+ return retval;
+}
-#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+ __block bool retval = false;
+ dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (dyld3::dyld_get_base_platform(platform) == version.platform && min_version >= version.version) {
+ retval = true;
+ }
+ });
+ return retval;
+}
-#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+ return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version);
+}
-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;
+bool dyld_program_minos_at_least(dyld_build_version_t version) {
+ return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version);
+}
+
+static
+uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) {
+ __block uint32_t retval = 0;
+ ((MachOLoaded*)mh)->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+ if (strcmp(loadPath, installname) == 0) {
+ retval = currentVersion;
+ stop = true;
+ }
});
+ return retval;
+}
+#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+
+static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
+ // This is a binary without a version load command, we need to infer things
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[] = {
+ uint32_t linkedVersion = 0;
+#if TARGET_OS_OSX
+ linkedVersion = linkedDylibVersion(mh, "/usr/lib/libSystem.B.dylib");
+ static const DylibToOSMapping versionMapping[] = {
{ PACKED_VERSION(88,1,3), 0x000A0400 },
{ PACKED_VERSION(111,0,0), 0x000A0500 },
{ PACKED_VERSION(123,0,0), 0x000A0600 },
// We don't need to expand this table because all recent
// binaries have LC_VERSION_MIN_ load command.
};
-
- if ( libSystemVers != 0 ) {
+#elif TARGET_OS_IOS
+ linkedVersion = linkedDylibVersion(mh, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation");
+ static const DylibToOSMapping versionMapping[] = {
+ { 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.
+ };
+#else
+ static const DylibToOSMapping versionMapping[] = {};
+#endif
+ if ( linkedVersion != 0 ) {
uint32_t lastOsVersion = 0;
- for (const DylibToOSMapping* p=libSystemMapping; ; ++p) {
- if ( p->dylibVersion == 0 )
+ for (const DylibToOSMapping* p=versionMapping; ; ++p) {
+ if ( p->dylibVersion == 0 ) {
return p->osVersion;
- if ( libSystemVers < p->dylibVersion )
+ }
+ if ( linkedVersion < p->dylibVersion ) {
return lastOsVersion;
+ }
lastOsVersion = p->osVersion;
}
}
- #endif
- return 0;
+ 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)
+// assumes mh has already been validated
+static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
{
- 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;
+ const MachOFile* mf = (MachOFile*)mh;
+ __block bool lcFound = false;
+ mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
+ lcFound = true;
+ // If SDK field is empty then derive the value from library linkages
+ if (sdk == 0) {
+ sdk = deriveVersionFromDylibs(mh);
}
- }
+ callback((const dyld_platform_t)platform, sdk, minOS);
+ });
-#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;
+ // No load command was found, so again, fallback to deriving it from library linkages
+ if (!lcFound) {
+#if TARGET_OS_IOS
+#if __x86_64__ || __x86__
+ dyld_platform_t platform = PLATFORM_IOSSIMULATOR;
#else
- // MacOSX and iOS have old binaries without version load commmand.
- return deriveSDKVersFromDylibs(mh);
+ dyld_platform_t platform = PLATFORM_IOS;
#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;
+#elif TARGET_OS_OSX
+ dyld_platform_t platform = PLATFORM_MACOS;
#else
- case Platform::macOS:
- return minOS;
+ dyld_platform_t platform = 0;
#endif
- default:
- // wrong binary for this platform
- break;
+ uint32_t derivedVersion = deriveVersionFromDylibs(mh);
+ if ( platform != 0 && derivedVersion != 0 ) {
+ callback(platform, derivedVersion, 0);
}
}
- return 0;
}
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
+{
+ Diagnostics diag;
+ const MachOFile* mf = (MachOFile*)mh;
+ if ( mf->isMachO(diag, mh->sizeofcmds + sizeof(mach_header_64)) )
+ dyld_get_image_versions_internal(mh, callback);
+}
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());
+ log_apis("dyld_get_program_min_os_version()\n");
+ static uint32_t sProgramMinVersion = 0;
+ if (sProgramMinVersion == 0) {
+ sProgramMinVersion = dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
+ }
+ return sProgramMinVersion;
}
-
bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
{
- log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
+ log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
- if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+ const MachOFile* mf = (MachOFile*)mh;
+ if ( !mf->hasMachOMagic() )
return false;
- MachOParser parser(mh);
- return parser.getUuid(uuid);
+
+ return mf->getUuid(uuid);
}
//
//
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;
+ log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
+
+ const closure::Image* mainImage = gAllImages.mainExecutableImage();
+ const char* path = gAllImages.imagePath(mainImage);
+ size_t pathSize = strlen(path) + 1;
+ if ( *bufsize >= pathSize ) {
+ strcpy(buf, path);
+ return 0;
}
+ *bufsize = (uint32_t)pathSize;
return -1;
}
{
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;
+ addr = stripPointer(addr);
+
+ const MachOLoaded* ml;
+ if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) )
+ return ml;
+
return nullptr;
}
{
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;
+ addr = stripPointer(addr);
+ const char* result = gAllImages.pathForImageMappedAt(addr);
+
+ log_apis(" dyld_image_path_containing_address() => %s\n", result);
+ return result;
}
+
+
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);
+ return gAllImages.immutableMemory(addr, length);
}
{
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 ) {
+ // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
+ if ( info == NULL )
+ return 0; // failure
+
+ addr = stripPointer(addr);
+
+ __block int result = 0;
+ const MachOLoaded* ml = nullptr;
+ const char* path = nullptr;
+ if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, &path) ) {
+ info->dli_fname = path;
+ info->dli_fbase = (void*)ml;
+
+ uint64_t symbolAddr;
+ if ( addr == info->dli_fbase ) {
+ // special case lookup of header
+ info->dli_sname = "__dso_handle";
+ info->dli_saddr = info->dli_fbase;
+ }
+ else if ( ml->findClosestSymbol((long)addr, &(info->dli_sname), &symbolAddr) ) {
+ info->dli_saddr = (void*)(long)symbolAddr;
+ // 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;
}
- // 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;
+ result = 1;
}
- log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
- return 1;
+
+ if ( result == 0 )
+ log_apis(" dladdr() => 0\n");
+ else
+ log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
+ return result;
}
#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);
}
VIS_HIDDEN
-void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue)
+void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue)
{
*dontContinue = (((uintptr_t)h) & 1);
- *mh = (const mach_header*)((((uintptr_t)h) & (-2)) << 5);
+ *mh = (const MachOLoaded*)((((uintptr_t)h) & (-2)) << 5);
}
int dlclose(void* handle)
{
+ DYLD_LOAD_LOCK_THIS_BLOCK
log_apis("dlclose(%p)\n", handle);
// silently accept magic handles for main executable
if ( handle == RTLD_DEFAULT )
return 0;
- // from here on, serialize all dlopen()s
- RecursiveAutoLock dlopenSerializer;
-
- const mach_header* mh;
+ const MachOLoaded* 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);
+
+ __block bool unloadable = false;
+ __block bool validHandle = false;
+ gAllImages.infoForImageMappedAt(mh, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ validHandle = true;
+ if ( !foundImage.image()->neverUnload() )
+ unloadable = true;
+ });
+ if ( unloadable ) {
+ gAllImages.decRefCount(mh); // removes image if reference count went to zero
+ }
+
+ if ( validHandle ) {
clearErrorString();
return 0;
}
}
-
-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)
+void* dlopen_internal(const char* path, int mode, void* callerAddress)
{
+ DYLD_LOAD_LOCK_THIS_BLOCK
log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
clearErrorString();
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 __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
+ char canonicalPath[PATH_MAX];
+ if ( leafName != path ) {
+ // make path canonical if it contains a // or ./
+ if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+ const char* lastSlash = strrchr(path, '/');
+ char dirPath[PATH_MAX];
+ if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+ dirPath[lastSlash-path] = '\0';
+ if ( realpath(dirPath, canonicalPath) ) {
+ strlcat(canonicalPath, "/", sizeof(canonicalPath));
+ if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+ // if all fit in buffer, use new canonical path
+ path = canonicalPath;
+ }
+ }
+ }
}
- });
- if ( alreadyLoadMH != nullptr) {
- void* result = makeDlHandle(alreadyLoadMH, dontContinue);
- log_apis(" dlopen(%s) => %p\n", leafName, result);
- return result;
}
+#endif
- // 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;
- }
- }
+ // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
+ const bool firstOnly = (mode & RTLD_FIRST);
- // 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;
- });
- }
+ // RTLD_LOCAL means when flat searches of all images (e.g. RTLD_DEFAULT) is done, this image should be skipped. But dlsym(handle, xx) can find symbols
+ const bool rtldLocal = (mode & RTLD_LOCAL);
- // 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_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
+ // Note: this is a weird state and it slightly different semantics that other OSs
+ const bool rtldNoDelete = (mode & RTLD_NODELETE);
// RTLD_NOLOAD means do nothing if image not already loaded
- if ( mode & RTLD_NOLOAD ) {
- log_apis(" dlopen(%s) => NULL\n", leafName);
+ const bool rtldNoLoad = (mode & RTLD_NOLOAD);
+
+ // try to load image from specified path
+ Diagnostics diag;
+ const mach_header* topLoadAddress = gAllImages.dlopen(diag, path, rtldNoLoad, rtldLocal, rtldNoDelete, false, callerAddress);
+ if ( diag.hasError() ) {
+ setErrorString("dlopen(%s, 0x%04X): %s", path, mode, diag.errorMessage());
+ log_apis(" dlopen: closure creation error: %s\n", diag.errorMessage());
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;
+ if ( topLoadAddress == nullptr ) {
+ log_apis(" dlopen(%s) => NULL\n", leafName);
+ return nullptr;
}
- for (int i=0; i < closuredErrorMessagesCount;++i)
- free((void*)closuredErrorMessages[i]);
-
- log_apis(" dlopen(%s) => NULL\n", leafName);
+ void* result = makeDlHandle(topLoadAddress, firstOnly);
+ log_apis(" dlopen(%s) => %p\n", leafName, result);
+ return result;
- return nullptr;
}
-bool dlopen_preflight(const char* path)
+bool dlopen_preflight_internal(const char* path)
{
+ DYLD_LOAD_LOCK_THIS_BLOCK
log_apis("dlopen_preflight(%s)\n", path);
- if ( gAllImages.alreadyLoaded(path, false) != nullptr )
+ // check if path is in dyld shared cache
+ if ( gAllImages.dyldCacheHasPath(path) )
return true;
- if ( gAllImages.findImageInKnownGroups(path) != nullptr )
+ // check if file is loadable
+ Diagnostics diag;
+ closure::FileSystemPhysical fileSystem;
+ closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, MachOFile::currentArchName(), MachOFile::currentPlatform());
+ if ( loadedFileInfo.fileContent != nullptr ) {
+ fileSystem.unloadFile(loadedFileInfo);
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;
+ return false;
}
-static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower)
+static void* dlsym_search(const char* symName, const LoadedImage& start, bool searchStartImage, MachOLoaded::DependentToMachOLoaded reExportHelper,
+ bool* resultPointsToInstructions)
{
- // 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);
+ MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+ return gAllImages.findDependent(mh, depIndex);
+ };
+ //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
+ // walk all dependents of 'start' in order looking for symbol
__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) )
+ gAllImages.visitDependentsTopDown(start, ^(const LoadedImage& aLoadedImage, bool& stop) {
+ //fprintf(stderr, " search: %s\n", aLoadedImage.image()->path());
+ if ( !searchStartImage && aLoadedImage.image() == start.image() )
return;
- if ( loadAddress != nullptr ) {
- MachOParser parser(loadAddress);
- if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) {
- stop = true;
- }
+ if ( aLoadedImage.loadedAddress()->hasExportedSymbol(symName, finder, &result, resultPointsToInstructions) ) {
+ 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)
+
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
{
log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
clearErrorString();
+ MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+ return gAllImages.findDependent(mh, depIndex);
+ };
+
// 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];
+ BLOCK_ACCCESSIBLE_ARRAY(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);
- };
-
+ __block void* result = nullptr;
+ __block bool resultPointsToInstructions = false;
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;
- }
+ gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
+ if ( loadedImage.hideFromFlatSearch() )
+ return;
+ if ( loadedImage.loadedAddress()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
+ stop = true;
}
+ });
+ if ( result != nullptr ) {
+#if __has_feature(ptrauth_calls)
+ if (resultPointsToInstructions)
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
+ log_apis(" dlsym() => %p\n", result);
+ return result;
}
setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
log_apis(" dlsym() => NULL\n");
}
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) ) {
+ if ( gAllImages.mainExecutable()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
log_apis(" dlsym() => %p\n", result);
+#if __has_feature(ptrauth_calls)
+ if (resultPointsToInstructions)
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
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() ) {
+ __block bool foundCaller = false;
+ gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ foundCaller = true;
+ result = dlsym_search(underscoredName, foundImage, false, finder, &resultPointsToInstructions);
+ });
+ if ( !foundCaller ) {
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() ) {
+ __block bool foundCaller = false;
+ gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ foundCaller = true;
+ result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
+ });
+ if ( !foundCaller ) {
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() ) {
+ const MachOLoaded* mh;
+ bool dontContinue;
+ parseDlHandle(handle, &mh, &dontContinue);
+
+ __block bool foundCaller = false;
+ gAllImages.infoForImageWithLoadAddress(mh, ^(const LoadedImage& foundImage) {
+ foundCaller = true;
+ if ( dontContinue ) {
+ // RTLD_FIRST only searches one place
+ // we go through infoForImageWithLoadAddress() to validate the handle
+ mh->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions);
+ }
+ else {
+ result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
+ }
+ });
+ if ( !foundCaller ) {
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 ) {
+#if __has_feature(ptrauth_calls)
+ if (resultPointsToInstructions)
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
log_apis(" dlsym() => %p\n", result);
return result;
}
return NULL;
}
-bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[])
{
- 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;
+ log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos);
+
+ // in stack crawls, common for contiguous fames to be in same image, so cache
+ // last lookup and check if next addresss in in there before doing full search
+ const MachOLoaded* ml = nullptr;
+ uint64_t textSize = 0;
+ const void* end = (void*)ml;
+ for (unsigned i=0; i < count; ++i) {
+ const void* addr = stripPointer(addresses[i]);
+ bzero(&infos[i], sizeof(dyld_image_uuid_offset));
+ if ( (ml == nullptr) || (addr < (void*)ml) || (addr > end) ) {
+ if ( gAllImages.infoForImageMappedAt(addr, &ml, &textSize, nullptr) ) {
+ end = (void*)((uint8_t*)ml + textSize);
}
- else if ( strcmp(sectName, "__unwind_info") == 0 ) {
- info->compact_unwind_section = content;
- info->compact_unwind_section_length = sectSize;
+ else {
+ ml = nullptr;
+ textSize = 0;
}
}
- });
+ if ( ml != nullptr ) {
+ infos[i].image = ml;
+ infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)ml;
+ ml->getUuid(infos[i].uuid);
+ }
+ }
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+ gAllImages.addLoadNotifier(func);
+}
- return true;
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+{
+ log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
+ addr = (void*)stripPointer(addr);
+
+ const MachOLoaded* ml = nullptr;
+ if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) ) {
+ info->mh = ml;
+ info->dwarf_section = nullptr;
+ info->dwarf_section_length = 0;
+ info->compact_unwind_section = nullptr;
+ info->compact_unwind_section_length = 0;
+
+ uint64_t size;
+ if ( const void* content = ml->findSectionContent("__TEXT", "__eh_frame", size) ) {
+ info->dwarf_section = content;
+ info->dwarf_section_length = (uintptr_t)size;
+ }
+ if ( const void* content = ml->findSectionContent("__TEXT", "__unwind_info", size) ) {
+ info->compact_unwind_section = content;
+ info->compact_unwind_section_length = (uintptr_t)size;
+ }
+ return true;
+ }
+
+ return false;
}
bool dyld_process_is_restricted()
{
log_apis("dyld_process_is_restricted()\n");
-
- launch_cache::Closure closure(gAllImages.mainClosure());
- return closure.isRestricted();
+ return gAllImages.isRestricted();
}
});
// iterate all images
- sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) {
+ sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop) {
dyld_shared_cache_dylib_text_info dylibTextInfo;
dylibTextInfo.version = 2;
dylibTextInfo.loadAddressUnslid = loadAddressUnslid;
#include <string.h>
#include <stdint.h>
+#include <pthread.h>
+#include <uuid/uuid.h>
#include "dlfcn.h"
#include "dyld_priv.h"
#define TEMP_HIDDEN __attribute__((visibility("hidden")))
+//
+// The implementation of all dyld load/unload API's must hold a global lock
+// so that the next load/unload does start until the current is complete.
+// This lock is recursive so that initializers can call dlopen().
+// This is done using the macros DYLD_LOCK_THIS_BLOCK.
+// Example:
+//
+// void dyld_load_api() {
+// DYLD_LOAD_LOCK_THIS_BLOCK;
+// // free to do stuff here
+// // that accesses dyld internal data structures
+// }
+//
+//
+
+#define DYLD_LOAD_LOCK_THIS_BLOCK RecursiveAutoLock _dyld_load_lock;
+
namespace dyld3 {
+class __attribute__((visibility("hidden"))) RecursiveAutoLock
+{
+public:
+ RecursiveAutoLock() {
+ pthread_mutex_lock(&_sMutex);
+ }
+ ~RecursiveAutoLock() {
+ pthread_mutex_unlock(&_sMutex);
+ }
+private:
+ static pthread_mutex_t _sMutex;
+};
+
+
+
uint32_t _dyld_image_count() 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_min_os_version() TEMP_HIDDEN;
+dyld_platform_t dyld_get_active_platform(void) TEMP_HIDDEN;
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) TEMP_HIDDEN;
+bool dyld_is_simulator_platform(dyld_platform_t platform) TEMP_HIDDEN;
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_program_sdk_at_least(dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_program_minos_at_least(dyld_build_version_t version) TEMP_HIDDEN;
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) TEMP_HIDDEN;
bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) TEMP_HIDDEN;
int dlclose(void* handle) TEMP_HIDDEN;
-void* dlopen(const char* path, int mode) TEMP_HIDDEN;
+void* dlopen_internal(const char* path, int mode, void* callerAddress) TEMP_HIDDEN;
-bool dlopen_preflight(const char* path) TEMP_HIDDEN;
+bool dlopen_preflight_internal(const char* path) TEMP_HIDDEN;
-void* dlsym(void* handle, const char* symbolName) TEMP_HIDDEN;
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) TEMP_HIDDEN;
const struct dyld_all_image_infos* _dyld_get_all_image_infos() TEMP_HIDDEN;
const void* _dyld_get_shared_cache_range(size_t* length) TEMP_HIDDEN;
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]) TEMP_HIDDEN;
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable)) TEMP_HIDDEN;
+
bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) TEMP_HIDDEN;
bool dyld_process_is_restricted() TEMP_HIDDEN;
#include "dyld_priv.h"
#include "AllImages.h"
-#include "MachOParser.h"
#include "Loading.h"
#include "Logging.h"
#include "Diagnostics.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);
+void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue);
// only in macOS and deprecated
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;
+ OFIInfo result;
+ result.path = strdup(path);
+ result.memSource = nullptr;
+ result.memLength = 0;
+ result.loadAddress = nullptr;
+ result.imageNum = 0;
+ *ofi = gAllImages.addNSObjectFileImage(result);
- log_apis("NSCreateObjectFileImageFromFile() => %p\n", result);
+ log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi);
return NSObjectFileImageSuccess;
}
// 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;
+
+ // check if it is current arch mach-o or fat with slice for current arch
+ bool usable = false;
+ const MachOFile* mf = (MachOFile*)memImage;
+ if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) {
+ if ( strcmp(mf->archName(), MachOFile::currentArchName()) == 0 )
+ usable = true;
+#if __x86_64__
+ // <rdar://problem/42727628> support thin x86_64 on haswell machines
+ else if ( (strcmp(MachOFile::currentArchName(), "x86_64h") == 0) && (strcmp(mf->archName(), "x86_64") == 0) )
+ usable = true;
+#endif
}
- 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;
+ else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) {
+ uint64_t sliceOffset;
+ uint64_t sliceLen;
+ bool missingSlice;
+ if ( ff->isFatFileWithSlice(diag, memImageSize, MachOFile::currentArchName(), sliceOffset, sliceLen, missingSlice) ) {
+ mf = (MachOFile*)((long)memImage+sliceOffset);
+ if ( mf->isMachO(diag, sliceLen) ) {
+ usable = true;
}
- });
+ }
+ }
+ if ( usable ) {
+ if ( !mf->supportsPlatform(Platform::macOS) )
+ usable = false;
}
- if ( foundMH == nullptr ) {
+ if ( !usable ) {
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);
+ if ( !mf->isBundle() ) {
+ log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
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;
+ OFIInfo result;
+ result.path = nullptr;
+ result.memSource = memImage;
+ result.memLength = memImageSize;
+ result.loadAddress = nullptr;
+ result.imageNum = 0;
+ *ofi = gAllImages.addNSObjectFileImage(result);
- log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result);
+ log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi);
return NSObjectFileImageSuccess;
}
NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
{
+ DYLD_LOAD_LOCK_THIS_BLOCK
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);
+ __block const char* path = nullptr;
+ bool foundImage = gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
+ // if this is memory based image, write to temp file, then use file based loading
+ if ( image.memSource != nullptr ) {
+ // make temp file with content of memory buffer
+ bool successfullyWritten = false;
+ image.path = ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-");
+ if ( image.path != nullptr ) {
+ int fd = ::open(image.path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if ( fd != -1 ) {
+ ssize_t writtenSize = ::pwrite(fd, image.memSource, image.memLength, 0);
+ if ( writtenSize == image.memLength )
+ successfullyWritten = true;
+ ::close(fd);
+ }
}
- }
- if ( !successfullyWritten ) {
- if ( ofi->path != nullptr ) {
- free((void*)ofi->path);
- ofi->path = nullptr;
+ if ( !successfullyWritten ) {
+ if ( image.path != nullptr ) {
+ free((void*)image.path);
+ image.path = nullptr;
+ }
+ log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
+ return;
}
- 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
- }
+ path = image.path;
+ });
- // 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]);
- }
+ if (!foundImage) {
+ // ofi is invalid if not in list
+ log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
+ return nullptr;
}
- // 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);
+ if (!path)
+ return nullptr;
+
+ // dlopen the binary outside of the read lock as we don't want to risk deadlock
+ Diagnostics diag;
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ const MachOLoaded* loadAddress = gAllImages.dlopen(diag, path, false, false, false, true, callerAddress);
+ if ( diag.hasError() ) {
+ log_apis(" NSLinkModule: failed: %s\n", diag.errorMessage());
+ return nullptr;
}
- log_apis("NSLinkModule() => %p\n", ofi->loadAddress);
- return (NSModule)ofi->loadAddress;
+ // Now update the load address of this object
+ gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
+ image.loadAddress = loadAddress;
+
+ // if memory based load, delete temp file
+ if ( image.memSource != nullptr ) {
+ log_apis(" NSLinkModule: delete temp file: %s\n", image.path);
+ ::unlink(image.path);
+ }
+ });
+
+ log_apis("NSLinkModule() => %p\n", loadAddress);
+ return (NSModule)loadAddress;
}
// NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
bool NSUnLinkModule(NSModule module, uint32_t options)
{
+ DYLD_LOAD_LOCK_THIS_BLOCK
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;
- }
+ __block const mach_header* mh = nullptr;
+ gAllImages.infoForImageMappedAt(module, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ mh = foundImage.loadedAddress();
+ });
- log_apis("NSUnLinkModule() => %d\n", result);
+ if ( mh != nullptr )
+ gAllImages.decRefCount(mh); // removes image if reference count went to zero
- return result;
+ log_apis("NSUnLinkModule() => %d\n", mh != nullptr);
+
+ return mh != nullptr;
}
// NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
-bool NSDestroyObjectFileImage(NSObjectFileImage ofi)
-{
- log_apis("NSDestroyObjectFileImage(%p)\n", ofi);
+bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle)
+{
+ log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle);
+
+ __block const void* memSource = nullptr;
+ __block size_t memLength = 0;
+ __block const char* path = nullptr;
+ bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+ // keep copy of info
+ memSource = image.memSource;
+ memLength = image.memLength;
+ path = image.path;
+ });
- // ofi is invalid if not in list
- if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ if (!foundImage)
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);
+ gAllImages.removeNSObjectFileImage(imageHandle);
// 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
halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
}
-bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi, const char* symbolName)
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage imageHandle, const char* symbolName)
{
- log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi, symbolName);
+ log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle, symbolName);
+
+ __block bool hasSymbol = false;
+ bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+ void* addr;
+ bool resultPointsToInstructions = false;
+ hasSymbol = image.loadAddress->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
+ });
// ofi is invalid if not in list
- if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ if (!foundImage)
return false;
- void* addr;
- MachOParser parser(ofi->loadAddress);
- return parser.hasExportedSymbol(symbolName, ^(uint32_t , const char*, void*, const mach_header**, void**) {
- return false;
- }, &addr);
+ return hasSymbol;
}
-void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi, const char* segmentName, const char* sectionName, size_t* size)
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle, const char* segmentName, const char* sectionName, size_t* size)
{
+ __block const void* result = nullptr;
+ bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+ uint64_t sz;
+ result = image.loadAddress->findSectionContent(segmentName, sectionName, sz);
+ *size = (size_t)sz;
+ });
+
// ofi is invalid if not in list
- if ( !gAllImages.hasNSObjectFileImage(ofi) )
+ if (!foundImage)
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;
+ return (void*)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;
+ __block const char* result = nullptr;
+ gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ result = gAllImages.imagePath(foundImage.image());
+ });
+
+ return result;
}
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;
-}
+ __block const char* result = nullptr;
+ gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ result = gAllImages.imagePath(foundImage.image());
+ });
+ return result;
+ }
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;
- }
+ __block bool result = false;
+ gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
+ bool resultPointsToInstructions = false;
+ if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) {
+ *foundInImageAtLoadAddress = loadedImage.loadedAddress();
+ stop = true;
+ result = true;
}
- }
- return false;
+ });
+ return result;
}
bool NSIsSymbolNameDefined(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);
+ void* addr;
+ bool resultPointsToInstructions = false;
+ return ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
}
NSSymbol NSLookupAndBindSymbol(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;
- }
+ const MachOLoaded* mh = (const MachOLoaded*)module;
+ void* addr;
+ bool resultPointsToInstructions = false;
+ if ( mh->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
+ return (NSSymbol)addr;
}
return nullptr;
}
-NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options)
+NSSymbol NSLookupSymbolInImage(const 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;
- }
-
+ void* addr;
+ bool resultPointsToInstructions = false;
+ if ( ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
+ log_apis(" NSLookupSymbolInImage() => %p\n", addr);
+ return (NSSymbol)addr;
+ }
if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
log_apis(" NSLookupSymbolInImage() => NULL\n");
return nullptr;
}
+ // FIXME: abort();
return nullptr;
}
{
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;
+ __block NSModule result = nullptr;
+ gAllImages.infoForImageMappedAt(symbol, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ result = (NSModule)foundImage.loadedAddress();
+ });
+
+ return result;
}
void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString)
{
log_apis("NSAddLibrary(%s)\n", pathName);
- return ( dlopen(pathName, 0) != nullptr);
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
}
bool NSAddLibraryWithSearching(const char* pathName)
{
log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
- return ( dlopen(pathName, 0) != nullptr);
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
}
const mach_header* NSAddImage(const char* imageName, uint32_t options)
if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 )
dloptions |= RTLD_NOLOAD;
- void* h = dlopen(imageName, dloptions);
+ void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+ void* h = dlopen_internal(imageName, dloptions, callerAddress);
if ( h != nullptr ) {
- const mach_header* mh;
+ const MachOLoaded* mh;
bool dontContinue;
parseDlHandle(h, &mh, &dontContinue);
return mh;
#include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.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"
+#include "Closure.h"
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
extern const char** appleParams;
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());
-}
+///////////////////// AllImages ////////////////////////////
-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
+AllImages gAllImages;
-///////////////////// ChunkedVector ////////////////////////////
-template <typename T, int C>
-ChunkedVector<T,C>* ChunkedVector<T,C>::make(uint32_t count)
+void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+ const Array<LoadedImage>& initialImages)
{
- 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;
- }
-}
+ _mainClosure = closure;
+ _initialImages = &initialImages;
+ _dyldCacheAddress = dyldCacheLoadAddress;
+ _dyldCachePath = dyldCachePath;
-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;
+ if ( _dyldCacheAddress ) {
+ const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)((uint64_t)_dyldCacheAddress + _dyldCacheAddress->header.mappingOffset);
+ _dyldCacheSlide = (uint64_t)dyldCacheLoadAddress - fileMappings[0].address;
+ _imagesArrays.push_back(dyldCacheLoadAddress->cachedDylibsImageArray());
+ if ( auto others = dyldCacheLoadAddress->otherOSImageArray() )
+ _imagesArrays.push_back(others);
}
-}
-
-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;
-}
+ _imagesArrays.push_back(_mainClosure->images());
-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);
+ // record first ImageNum to do use for dlopen() calls
+ _mainClosure->images()->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ closure::ImageNum num = image->imageNum();
+ if ( num >= _nextImageNum )
+ _nextImageNum = num+1;
+ });
+
+ // Make temporary old image array, so libSystem initializers can be debugged
+ STACK_ALLOC_ARRAY(dyld_image_info, oldDyldInfo, initialImages.count());
+ for (const LoadedImage& li : initialImages) {
+ oldDyldInfo.push_back({li.loadedAddress(), li.image()->path(), 0});
}
- _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);
-}
+ _oldAllImageInfos->infoArray = &oldDyldInfo[0];
+ _oldAllImageInfos->infoArrayCount = (uint32_t)oldDyldInfo.count();
+ _oldAllImageInfos->notification(dyld_image_adding, _oldAllImageInfos->infoArrayCount, _oldAllImageInfos->infoArray);
+ _oldAllImageInfos->infoArray = nullptr;
+ _oldAllImageInfos->infoArrayCount = 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);
+ _processDOFs = Loader::dtraceUserProbesEnabled();
}
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::acquireWriteLock()
+void AllImages::setProgramVars(ProgramVars* vars)
{
- assert(pthread_rwlock_wrlock(&_lock) == 0);
+ _programVars = vars;
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)_programVars->mh;
+ mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
+ _platform = (dyld_platform_t)platform;
+ //FIXME assert there is only one?
+ });
}
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::releaseWriteLock()
+void AllImages::setRestrictions(bool allowAtPaths, bool allowEnvPaths)
{
- assert(pthread_rwlock_unlock(&_lock) == 0);
+ _allowAtPaths = allowAtPaths;
+ _allowEnvPaths = allowEnvPaths;
}
-template <typename T, int C>
-uint32_t ReaderWriterChunkedVector<T,C>::count() const
+void AllImages::applyInitialImages()
{
- __block uint32_t result = 0;
- withReadLock(^() {
- for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
- result += chunk->count();
- }
- });
- return result;
+ addImages(*_initialImages);
+ runImageNotifiers(*_initialImages);
+ _initialImages = nullptr; // this was stack allocated
}
-template <typename T, int C>
-uint32_t ReaderWriterChunkedVector<T,C>::countNoLock() const
+void AllImages::withReadLock(void (^work)()) const
{
- uint32_t result = 0;
- for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
- result += chunk->count();
- }
- return result;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+ os_unfair_recursive_lock_lock(&_loadImagesLock);
+ work();
+ os_unfair_recursive_lock_unlock(&_loadImagesLock);
+#else
+ pthread_mutex_lock(&_loadImagesLock);
+ work();
+ pthread_mutex_unlock(&_loadImagesLock);
+#endif
}
-template <typename T, int C>
-T* ReaderWriterChunkedVector<T,C>::addNoLock(uint32_t count, const T values[])
+void AllImages::withWriteLock(void (^work)())
{
- 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;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+ os_unfair_recursive_lock_lock(&_loadImagesLock);
+ work();
+ os_unfair_recursive_lock_unlock(&_loadImagesLock);
+#else
+ pthread_mutex_lock(&_loadImagesLock);
+ work();
+ pthread_mutex_unlock(&_loadImagesLock);
+#endif
}
-template <typename T, int C>
-T* ReaderWriterChunkedVector<T,C>::add(uint32_t count, const T values[])
+void AllImages::withNotifiersLock(void (^work)()) const
{
- __block T* result = nullptr;
- withWriteLock(^() {
- result = addNoLock(count, values);
- });
- return result;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+ os_unfair_recursive_lock_lock(&_notifiersLock);
+ work();
+ os_unfair_recursive_lock_unlock(&_notifiersLock);
+#else
+ pthread_mutex_lock(&_notifiersLock);
+ work();
+ pthread_mutex_unlock(&_notifiersLock);
+#endif
}
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::remove(const T& valueToRemove)
+void AllImages::mirrorToOldAllImageInfos()
{
- __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;
- }
+ withReadLock(^(){
+ // set infoArray to NULL to denote it is in-use
+ _oldAllImageInfos->infoArray = nullptr;
+
+ // if array not large enough, re-alloc it
+ uint32_t imageCount = (uint32_t)_loadedImages.count();
+ 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;
}
- });
-}
-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;
+ // fill out array to mirror current image list
+ int index = 0;
+ for (const LoadedImage& li : _loadedImages) {
+ _oldAllImageArray[index].imageLoadAddress = li.loadedAddress();
+ _oldAllImageArray[index].imageFilePath = imagePath(li.image());
+ _oldAllImageArray[index].imageFileModDate = 0;
+ ++index;
}
- });
-}
-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;
- }
-}
+ // 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;
-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;
-
- if ( _dyldCacheAddress ) {
- const DyldSharedCache* cache = (DyldSharedCache*)_dyldCacheAddress;
- const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)((uint64_t)_dyldCacheAddress + cache->header.mappingOffset);
- _dyldCacheSlide = (uint64_t)dyldCacheLoadAddress - fileMappings[0].address;
- }
-
- // 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()
+void AllImages::addImages(const Array<LoadedImage>& newImages)
{
- // 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);
+ // copy into _loadedImages
+ withWriteLock(^(){
+ _loadedImages.append(newImages);
+ // if any image not in the shared cache added, recompute bounds
+ for (const LoadedImage& li : newImages) {
+ if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+ recomputeBounds();
+ break;
+ }
}
- _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)
+void AllImages::runImageNotifiers(const Array<LoadedImage>& 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();
- });
- }
+ 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);
+ for (uint32_t i=0; i < count; ++i) {
+ oldDyldInfo[i].imageLoadAddress = newImages[i].loadedAddress();
+ oldDyldInfo[i].imageFilePath = imagePath(newImages[i].image());
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));
+ for (const LoadedImage& li : newImages) {
+ log_loads("dyld: %s\n", imagePath(li.image()));
}
#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 ) {
+ for (const LoadedImage& li : newImages) {
+ const closure::Image* image = li.image();
+ struct stat stat_buf;
+ fsid_t fsid = {{ 0, 0 }};
+ fsobj_id_t fsobjid = { 0, 0 };
+ if ( !image->inDyldCache() && (stat(imagePath(image), &stat_buf) == 0) ) {
fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
- fsid = {{ stat_buf.st_dev, 0 }};
+ fsid = {{ stat_buf.st_dev, 0 }};
}
- kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress);
+ uuid_t uuid;
+ image->getUuid(uuid);
+ kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, &uuid, fsobjid, fsid, li.loadedAddress());
}
}
#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) {
- log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress);
- if (newImages[i].justUsedFromDyldCache) {
- func(newImages[i].loadAddress, _dyldCacheSlide);
- } else {
- MachOParser parser(newImages[i].loadAddress);
- func(newImages[i].loadAddress, parser.getSlide());
+ withNotifiersLock(^{
+ for (NotifyFunc func : _loadNotifiers) {
+ for (const LoadedImage& li : newImages) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+ if ( li.image()->inDyldCache() )
+ func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+ else
+ func(li.loadedAddress(), li.loadedAddress()->getSlide());
}
}
- }
+ for (LoadNotifyFunc func : _loadNotifiers2) {
+ for (const LoadedImage& li : newImages) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+ if ( li.image()->inDyldCache() )
+ func(li.loadedAddress(), li.image()->path(), false);
+ else
+ func(li.loadedAddress(), li.image()->path(), !li.image()->neverUnload());
+ }
+ }
+ });
// 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;
+ for (const LoadedImage& li : newImages) {
+ const closure::Image* image = li.image();
+ if ( image->hasObjC() ) {
+ pathsBuffer[imagesWithObjC] = imagePath(image);
+ mhBuffer[imagesWithObjC] = li.loadedAddress();
++imagesWithObjC;
}
}
if ( imagesWithObjC != 0 ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 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) {
notifyMonitorLoads(newImages);
}
-void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages)
+void AllImages::removeImages(const Array<LoadedImage>& 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());
+ withNotifiersLock(^{
+ for (NotifyFunc func : _unloadNotifiers) {
+ for (const LoadedImage& li : unloadImages) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+ log_notifications("dyld: remove notifier %p called with mh=%p\n", func, li.loadedAddress());
+ if ( li.image()->inDyldCache() )
+ func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+ else
+ func(li.loadedAddress(), li.loadedAddress()->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));
+ for (const LoadedImage& li : unloadImages) {
+ if ( li.image()->hasObjC() ) {
+ (*_objcNotifyUnmapped)(imagePath(li.image()), li.loadedAddress());
+ log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", li.loadedAddress(), imagePath(li.image()));
}
}
}
#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 ) {
+ for (const LoadedImage& li : unloadImages) {
+ const closure::Image* image = li.image();
+ struct stat stat_buf;
+ fsid_t fsid = {{ 0, 0 }};
+ fsobj_id_t fsobjid = { 0, 0 };
+ if ( stat(imagePath(image), &stat_buf) == 0 ) {
fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
- fsid = {{ stat_buf.st_dev, 0 }};
+ fsid = {{ stat_buf.st_dev, 0 }};
}
- kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress);
+ uuid_t uuid;
+ image->getUuid(uuid);
+ kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, &uuid, fsobjid, fsid, li.loadedAddress());
}
}
#endif
- // remove each from sLoadedImages
- for (uint32_t i=0; i < count; ++i) {
- LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData);
- sLoadedImages.remove(info);
- }
+ // remove each from _loadedImages
+ withWriteLock(^(){
+ for (const LoadedImage& uli : unloadImages) {
+ for (LoadedImage& li : _loadedImages) {
+ if ( uli.loadedAddress() == li.loadedAddress() ) {
+ _loadedImages.erase(li);
+ break;
+ }
+ }
+ }
+ recomputeBounds();
+ });
// sync to old all image infos struct
- sLoadedImages.withReadLock(^{
- mirrorToOldAllImageInfos();
- });
+ 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));
+ STACK_ALLOC_ARRAY(dyld_image_info, oldDyldInfo, unloadImages.count());
+ for (const LoadedImage& li : unloadImages) {
+ oldDyldInfo.push_back({li.loadedAddress(), li.image()->path(), 0});
}
+ _oldAllImageInfos->notification(dyld_image_removing, (uint32_t)oldDyldInfo.count(), &oldDyldInfo[0]);
// notify any processes tracking loads in this process
notifyMonitorUnloads(unloadImages);
+
+ // finally, unmap images
+ for (const LoadedImage& li : unloadImages) {
+ if ( li.leaveMapped() ) {
+ log_loads("dyld: unloaded but left mmapped %s\n", imagePath(li.image()));
+ }
+ else {
+ // unmapImage() modifies parameter, so use copy
+ LoadedImage copy = li;
+ Loader::unmapImage(copy);
+ log_loads("dyld: unloaded %s\n", imagePath(li.image()));
+ }
+ }
+}
+
+// must be called with writeLock held
+void AllImages::recomputeBounds()
+{
+ _lowestNonCached = UINTPTR_MAX;
+ _highestNonCached = 0;
+ for (const LoadedImage& li : _loadedImages) {
+ const MachOLoaded* ml = li.loadedAddress();
+ uintptr_t start = (uintptr_t)ml;
+ if ( !((MachOAnalyzer*)ml)->inDyldCache() ) {
+ if ( start < _lowestNonCached )
+ _lowestNonCached = start;
+ uintptr_t end = start + (uintptr_t)(li.image()->vmSizeToMap());
+ if ( end > _highestNonCached )
+ _highestNonCached = end;
+ }
+ }
+}
+
+uint32_t AllImages::count() const
+{
+ return (uint32_t)_loadedImages.count();
+}
+
+bool AllImages::dyldCacheHasPath(const char* path) const
+{
+ uint32_t dyldCacheImageIndex;
+ if ( _dyldCacheAddress != nullptr )
+ return _dyldCacheAddress->hasImagePath(path, dyldCacheImageIndex);
+ return false;
+}
+
+const char* AllImages::imagePathByIndex(uint32_t index) const
+{
+ if ( index < _loadedImages.count() )
+ return imagePath(_loadedImages[index].image());
+ return nullptr;
}
-void AllImages::setNeverUnload(const loader::ImageInfo& existingImage)
+const mach_header* AllImages::imageLoadAddressByIndex(uint32_t index) const
{
- sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) {
- if (value.image() == existingImage.imageData) {
- value.setNeverUnload();
- stop = true;
+ if ( index < _loadedImages.count() )
+ return _loadedImages[index].loadedAddress();
+ return nullptr;
+}
+
+bool AllImages::findImage(const mach_header* loadAddress, LoadedImage& foundImage) const
+{
+ __block bool result = false;
+ withReadLock(^(){
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.loadedAddress() == loadAddress ) {
+ foundImage = li;
+ result = true;
+ break;
+ }
}
});
+ return result;
}
-uint32_t AllImages::count() const
+void AllImages::forEachImage(void (^handler)(const LoadedImage& loadedImage, bool& stop)) const
{
- return sLoadedImages.count();
+ withReadLock(^{
+ bool stop = false;
+ for (const LoadedImage& li : _loadedImages) {
+ handler(li, stop);
+ if ( stop )
+ break;
+ }
+ });
}
-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;
+const char* AllImages::pathForImageMappedAt(const void* addr) const
+{
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so _loadedImages not allocated yet
+ for (const LoadedImage& li : *_initialImages) {
+ uint8_t permissions;
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ return li.image()->path();
+ }
+ }
+ return nullptr;
+ }
+
+ // if address is in cache, do fast search of TEXT segments in cache
+ __block const char* result = nullptr;
+ if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+ if ( addr < (void*)((uint8_t*)_dyldCacheAddress+_dyldCacheAddress->mappedSize()) ) {
+ uint64_t cacheSlide = (uint64_t)_dyldCacheAddress - _dyldCacheAddress->unslidLoadAddress();
+ uint64_t unslidTargetAddr = (uint64_t)addr - cacheSlide;
+ _dyldCacheAddress->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
+ if ( (loadAddressUnslid <= unslidTargetAddr) && (unslidTargetAddr < loadAddressUnslid+textSegmentSize) ) {
+ result = installName;
+ stop = true;
+ }
+ });
+ if ( result != nullptr )
+ return result;
+ }
+ }
+
+ // slow path - search image list
+ infoForImageMappedAt(addr, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ result = foundImage.image()->path();
+ });
+
+ return result;
+}
+
+void AllImages::infoForImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const
+{
+ __block uint8_t permissions;
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so _loadedImages not allocated yet
+ for (const LoadedImage& li : *_initialImages) {
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ handler(li, permissions);
+ break;
+ }
+ }
+ return;
+ }
+
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ handler(li, permissions);
+ break;
+ }
+ }
+ });
+}
+
+
+bool AllImages::infoForImageMappedAt(const void* addr, const MachOLoaded** ml, uint64_t* textSize, const char** path) const
+{
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so _loadedImages not allocated yet
+ for (const LoadedImage& li : *_initialImages) {
+ uint8_t permissions;
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ if ( ml != nullptr )
+ *ml = li.loadedAddress();
+ if ( path != nullptr )
+ *path = li.image()->path();
+ if ( textSize != nullptr ) {
+ *textSize = li.image()->textSize();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // if address is in cache, do fast search of TEXT segments in cache
+ __block bool result = false;
+ if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+ if ( addr < (void*)((uint8_t*)_dyldCacheAddress+_dyldCacheAddress->mappedSize()) ) {
+ uint64_t cacheSlide = (uint64_t)_dyldCacheAddress - _dyldCacheAddress->unslidLoadAddress();
+ uint64_t unslidTargetAddr = (uint64_t)addr - cacheSlide;
+ _dyldCacheAddress->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
+ if ( (loadAddressUnslid <= unslidTargetAddr) && (unslidTargetAddr < loadAddressUnslid+textSegmentSize) ) {
+ if ( ml != nullptr )
+ *ml = (MachOLoaded*)(loadAddressUnslid + cacheSlide);
+ if ( path != nullptr )
+ *path = installName;
+ if ( textSize != nullptr )
+ *textSize = textSegmentSize;
+ stop = true;
+ result = true;
+ }
+ });
+ if ( result )
+ return result;
+ }
+ }
+
+ // slow path - search image list
+ infoForImageMappedAt(addr, ^(const LoadedImage& foundImage, uint8_t permissions) {
+ if ( ml != nullptr )
+ *ml = foundImage.loadedAddress();
+ if ( path != nullptr )
+ *path = foundImage.image()->path();
+ if ( textSize != nullptr )
+ *textSize = foundImage.image()->textSize();
+ result = true;
+ });
+
+ return result;
+}
+
+// same as infoForImageMappedAt(), but only look at images not in the dyld cache
+void AllImages::infoForNonCachedImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const
+{
+ __block uint8_t permissions;
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so _loadedImages not allocated yet
+ for (const LoadedImage& li : *_initialImages) {
+ if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ handler(li, permissions);
+ break;
+ }
+ }
+ }
+ return;
+ }
+
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ handler(li, permissions);
+ break;
+ }
+ }
}
});
- return launch_cache::Image(foundImage);
}
-launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const
+bool AllImages::immutableMemory(const void* addr, size_t length) 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;
+ // quick check to see if in shared cache
+ if ( _dyldCacheAddress != nullptr ) {
+ bool readOnly;
+ if ( _dyldCacheAddress->inCache(addr, length, readOnly) ) {
+ return readOnly;
}
- });
- 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;
+ withReadLock(^() {
+ // quick check to see if it is not any non-cached image loaded
+ if ( ((uintptr_t)addr < _lowestNonCached) || ((uintptr_t)addr+length > _highestNonCached) ) {
+ result = false;
+ return;
+ }
+ // slow walk through all images, only look at images not in dyld cache
+ for (const LoadedImage& li : _loadedImages) {
+ if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+ uint8_t permissions;
+ if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+ result = ((permissions & VM_PROT_WRITE) == 0) && li.image()->neverUnload();
+ break;
+ }
+ }
}
});
+
return result;
}
-void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const
+void AllImages::infoForImageWithLoadAddress(const MachOLoaded* mh, void (^handler)(const LoadedImage& foundImage)) const
{
- sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) {
- handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop);
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.loadedAddress() == mh ) {
+ handler(li);
+ break;
+ }
+ }
});
}
-launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const
+bool AllImages::findImageNum(closure::ImageNum imageNum, LoadedImage& foundImage) 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;
+ if ( _initialImages != nullptr ) {
+ // being called during libSystem initialization, so _loadedImages not allocated yet
+ for (const LoadedImage& li : *_initialImages) {
+ if ( li.image()->representsImageNum(imageNum) ) {
+ foundImage = li;
+ return true;
}
}
- return launch_cache::Image(nullptr);
+ return false;
}
- // 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;
- }
+ bool result = false;
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.image()->representsImageNum(imageNum) ) {
+ foundImage = li;
+ result = true;
+ break;
}
}
- __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 result;
+}
+
+const MachOLoaded* AllImages::findDependent(const MachOLoaded* mh, uint32_t depIndex)
+{
+ __block const MachOLoaded* result = nullptr;
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.loadedAddress() == mh ) {
+ closure::ImageNum depImageNum = li.image()->dependentImageNum(depIndex);
+ LoadedImage depLi;
+ if ( findImageNum(depImageNum, depLi) )
+ result = depLi.loadedAddress();
+ break;
+ }
}
});
- return launch_cache::Image(foundImage);
+ return result;
}
-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;
+
+void AllImages::breadthFirstRecurseDependents(Array<closure::ImageNum>& visited, const LoadedImage& nodeLi, bool& stopped, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const
+{
+ // call handler on all direct dependents (unless already visited)
+ STACK_ALLOC_ARRAY(LoadedImage, dependentsToRecurse, 256);
+ nodeLi.image()->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& depStop) {
+ if ( kind == closure::Image::LinkKind::upward )
+ return;
+ if ( visited.contains(depImageNum) )
+ return;
+ LoadedImage depLi;
+ if ( !findImageNum(depImageNum, depLi) )
+ return;
+ handler(depLi, depStop);
+ visited.push_back(depImageNum);
+ if ( depStop ) {
+ stopped = true;
+ return;
}
+ dependentsToRecurse.push_back(depLi);
+ });
+ if ( stopped )
+ return;
+ // recurse on all dependents just visited
+ for (LoadedImage& depLi : dependentsToRecurse) {
+ breadthFirstRecurseDependents(visited, depLi, stopped, handler);
+ }
+}
+
+void AllImages::visitDependentsTopDown(const LoadedImage& start, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const
+{
+ withReadLock(^{
+ STACK_ALLOC_ARRAY(closure::ImageNum, visited, count());
+ bool stop = false;
+ handler(start, stop);
+ if ( stop )
+ return;
+ visited.push_back(start.image()->imageNum());
+ breadthFirstRecurseDependents(visited, start, stop, handler);
});
- return foundAddress;
}
-const mach_header* AllImages::mainExecutable() const
+const MachOLoaded* AllImages::mainExecutable() const
{
assert(_programVars != nullptr);
- return (const mach_header*)_programVars->mh;
+ return (const MachOLoaded*)_programVars->mh;
}
-launch_cache::Image AllImages::mainExecutableImage() const
+const closure::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;
+ return _mainClosure->images()->imageForNum(_mainClosure->topImage());
}
void AllImages::setMainPath(const char* path )
_mainExeOverridePath = path;
}
-const char* AllImages::imagePath(const BinaryImage* binImage) const
+const char* AllImages::imagePath(const closure::Image* image) 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() )
+ if ( image == mainExecutableImage() )
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;
- });
+ return image->path();
}
-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;
+dyld_platform_t AllImages::platform() const {
+ return _platform;
}
void AllImages::incRefCount(const mach_header* loadAddress)
{
- __block bool found = false;
- sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+ for (DlopenCount& entry : _dlopenRefCounts) {
if ( entry.loadAddress == loadAddress ) {
- found = true;
+ // found existing DlopenCount entry, bump counter
entry.refCount += 1;
- stop = true;
+ return;
}
- });
- if ( !found ) {
- DlopenCount newEnty = { loadAddress, 1 };
- sDlopenRefCounts.add(newEnty);
}
+
+ // no existing DlopenCount, add new one
+ _dlopenRefCounts.push_back({ loadAddress, 1 });
}
void AllImages::decRefCount(const mach_header* loadAddress)
{
- __block bool refCountNowZero = false;
- sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+ bool doCollect = false;
+ for (DlopenCount& entry : _dlopenRefCounts) {
if ( entry.loadAddress == loadAddress ) {
+ // found existing DlopenCount entry, bump counter
entry.refCount -= 1;
- stop = true;
- if ( entry.refCount == 0 )
- refCountNowZero = true;
+ if ( entry.refCount == 0 ) {
+ _dlopenRefCounts.erase(entry);
+ doCollect = true;
+ break;
+ }
+ return;
}
- });
- if ( refCountNowZero ) {
- DlopenCount delEnty = { loadAddress, 0 };
- sDlopenRefCounts.remove(delEnty);
- garbageCollectImages();
}
+ if ( doCollect )
+ garbageCollectImages();
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
-__NSObjectFileImage* AllImages::addNSObjectFileImage()
+NSObjectFileImage AllImages::addNSObjectFileImage(const OFIInfo& image)
{
- // 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;
- }
+ __block uint64_t imageNum = 0;
+ withWriteLock(^{
+ imageNum = ++_nextObjectFileImageNum;
+ _objectFileImages.push_back(image);
+ _objectFileImages.back().imageNum = imageNum;
});
- 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 (NSObjectFileImage)imageNum;
+}
+
+bool AllImages::forNSObjectFileImage(NSObjectFileImage imageHandle,
+ void (^handler)(OFIInfo& image)) {
+ uint64_t imageNum = (uint64_t)imageHandle;
+ bool __block foundImage = false;
+ withReadLock(^{
+ for (OFIInfo& ofi : _objectFileImages) {
+ if ( ofi.imageNum == imageNum ) {
+ handler(ofi);
+ foundImage = true;
+ return;
+ }
}
});
- return result;
+
+ return foundImage;
}
-void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
+void AllImages::removeNSObjectFileImage(NSObjectFileImage imageHandle)
{
- 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;
+ uint64_t imageNum = (uint64_t)imageHandle;
+ withWriteLock(^{
+ for (OFIInfo& ofi : _objectFileImages) {
+ if ( ofi.imageNum == imageNum ) {
+ _objectFileImages.erase(ofi);
+ return;
+ }
}
});
}
class VIS_HIDDEN Reaper
{
public:
- Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray);
+ struct ImageAndUse
+ {
+ const LoadedImage* li;
+ bool inUse;
+ };
+ Reaper(Array<ImageAndUse>& unloadables, AllImages*);
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;
+ Array<ImageAndUse>& _unloadables;
+ AllImages* _allImages;
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)
+Reaper::Reaper(Array<ImageAndUse>& unloadables, AllImages* all)
+ : _unloadables(unloadables), _allImages(all), _deadCount(0)
{
- 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;
+ for (AllImages::DlopenCount& entry : _allImages->_dlopenRefCounts) {
+ if ( entry.refCount != 0 ) {
+ for (ImageAndUse& iu : _unloadables) {
+ if ( iu.li->loadedAddress() == entry.loadAddress ) {
+ iu.inUse = true;
+ break;
+ }
}
- }
- });
+ }
+ }
}
uint32_t Reaper::inUseCount()
{
uint32_t count = 0;
- for (uint32_t i=0; i < _arrayCount; ++i) {
- if ( _inUseArray[i] )
+ for (ImageAndUse& iu : _unloadables) {
+ if ( iu.inUse )
++count;
}
return count;
}
-void Reaper::markDependentsOf(const LoadedImage* entry)
+void Reaper::markDependentsOf(const LoadedImage* li)
{
- 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;
+ li->image()->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
+ for (ImageAndUse& iu : _unloadables) {
+ if ( !iu.inUse && iu.li->image()->representsImageNum(depImageNum) ) {
+ iu.inUse = true;
+ break;
+ }
}
});
}
void Reaper::markDependentOfInUseImages()
{
- for (uint32_t i=0; i < _arrayCount; ++i) {
- if ( _inUseArray[i] )
- markDependentsOf(_unloadablesArray[i]);
+ for (ImageAndUse& iu : _unloadables) {
+ if ( iu.inUse )
+ markDependentsOf(iu.li);
}
}
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());
- }
+ //for (ImageAndUse& iu : _unloadables) {
+ // log(" in-used=%d %s\n", iu.inUse, iu.li->image()->path());
+ //}
}
void Reaper::garbageCollect()
lastCount = newCount;
} while (countChanged);
- _deadCount = _arrayCount - inUseCount();
+ _deadCount = (uint32_t)_unloadables.count() - inUseCount();
}
void Reaper::finalizeDeadImages()
__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] )
+ for (ImageAndUse& iu : _unloadables) {
+ if ( iu.inUse )
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) {
+ iu.li->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].addr = (char*)(iu.li->loadedAddress()) + vmOffset;
rangesArray[rangesCount].length = (size_t)vmSize;
++rangesCount;
}
// 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
+// Also note that this is done within the _loadedImages writer lock, so any dlopen/dlclose
// on other threads are blocked while this garbage collections runs
//
void AllImages::garbageCollectImages()
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;
+ STACK_ALLOC_ARRAY(Reaper::ImageAndUse, unloadables, _loadedImages.count());
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ if ( !li.image()->neverUnload() /*&& !li.neverUnload()*/ ) {
+ unloadables.push_back({&li, false});
+ //fprintf(stderr, "unloadable[%lu] %p %s\n", unloadables.count(), li.loadedAddress(), li.image()->path());
}
- });
- // make reaper object to do garbage collection and notifications
- Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray);
- reaper.garbageCollect();
+ }
+ });
+ // make reaper object to do garbage collection and notifications
+ Reaper reaper(unloadables, this);
+ reaper.garbageCollect();
- // FIXME: we should sort dead images so higher level ones are terminated first
+ // FIXME: we should sort dead images so higher level ones are terminated first
- // call cxa_finalize_ranges of dead images
- reaper.finalizeDeadImages();
+ // call cxa_finalize_ranges of dead images
+ reaper.finalizeDeadImages();
- // FIXME: call static terminators of dead images
+ // FIXME: call static terminators of dead images
- // FIXME: DOF unregister
- });
+ // 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());
- //});
+ //fprintf(stderr, "_loadedImages before GC removals:\n");
+ //for (const LoadedImage& li : _loadedImages) {
+ // fprintf(stderr, " loadAddr=%p, path=%s\n", li.loadedAddress(), li.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;
+ // because unloadables[] points into LoadedImage we are shrinking
+ STACK_ALLOC_ARRAY(LoadedImage, unloadImages, _loadedImages.count());
+ for (const Reaper::ImageAndUse& iu : unloadables) {
+ if ( !iu.inUse )
+ unloadImages.push_back(*iu.li);
}
- 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
+ // remove entries from _loadedImages
+ if ( !unloadImages.empty() ) {
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());
- //});
+ //fprintf(stderr, "_loadedImages after GC removals:\n");
+ //for (const LoadedImage& li : _loadedImages) {
+ // fprintf(stderr, " loadAddr=%p, path=%s\n", li.loadedAddress(), li.image()->path());
+ //}
}
// if some other thread called GC during our work, redo GC on its behalf
-VIS_HIDDEN
-const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount)
+void AllImages::addLoadNotifier(NotifyFunc func)
{
- __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();
+ // callback about already loaded images
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+ if ( li.image()->inDyldCache() )
+ func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+ else
+ func(li.loadedAddress(), li.loadedAddress()->getSlide());
}
- 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;
- }
+ // add to list of functions to call about future loads
+ withNotifiersLock(^{
+ _loadNotifiers.push_back(func);
});
- return result;
}
-bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const
+void AllImages::addUnloadNotifier(NotifyFunc func)
{
- // 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;
- }
+ // add to list of functions to call about future unloads
+ withNotifiersLock(^{
+ _unloadNotifiers.push_back(func);
});
- if ( foundAsNeverUnload )
- return false;
-
- return true;
}
-void AllImages::addLoadNotifier(NotifyFunc func)
+void AllImages::addLoadNotifier(LoadNotifyFunc 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();
+ withReadLock(^{
+ for (const LoadedImage& li : _loadedImages) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+ log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+ func(li.loadedAddress(), li.image()->path(), !li.image()->neverUnload());
+ }
});
- // 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);
+ withNotifiersLock(^{
+ _loadNotifiers2.push_back(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)
{
_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]);
+ uint32_t maxCount = count();
+ STACK_ALLOC_ARRAY(const mach_header*, mhs, maxCount);
+ STACK_ALLOC_ARRAY(const char*, paths, maxCount);
+ // don't need _mutex here because this is called when process is still single threaded
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.image()->hasObjC() ) {
+ paths.push_back(imagePath(li.image()));
+ mhs.push_back(li.loadedAddress());
+ }
+ }
+ if ( !mhs.empty() ) {
+ (*map)((uint32_t)mhs.count(), &paths[0], &mhs[0]);
+ if ( log_notifications("dyld: objc-mapped-notifier called with %ld images:\n", mhs.count()) ) {
+ for (uintptr_t i=0; i < mhs.count(); ++i) {
+ log_notifications("dyld: objc-mapped: %p %s\n", mhs[i], paths[i]);
}
}
}
}
-void AllImages::vmAccountingSetSuspended(bool suspend)
+void AllImages::applyInterposingToDyldCache(const closure::Closure* closure)
{
-#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);
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
+ const uintptr_t cacheStart = (uintptr_t)_dyldCacheAddress;
+ __block closure::ImageNum lastCachedDylibImageNum = 0;
+ __block const closure::Image* lastCachedDylibImage = nullptr;
+ __block bool suspendedAccounting = false;
+ closure->forEachPatchEntry(^(const closure::Closure::PatchEntry& entry) {
+ if ( entry.overriddenDylibInCache != lastCachedDylibImageNum ) {
+ lastCachedDylibImage = closure::ImageArray::findImage(imagesArrays(), entry.overriddenDylibInCache);
+ assert(lastCachedDylibImage != nullptr);
+ lastCachedDylibImageNum = entry.overriddenDylibInCache;
+ }
+ if ( !suspendedAccounting ) {
+ Loader::vmAccountingSetSuspended(true, log_fixups);
+ suspendedAccounting = true;
+ }
+ uintptr_t newValue = 0;
+ LoadedImage foundImage;
+ switch ( entry.replacement.image.kind ) {
+ case closure::Image::ResolvedSymbolTarget::kindImage:
+ assert(findImageNum(entry.replacement.image.imageNum, foundImage));
+ newValue = (uintptr_t)(foundImage.loadedAddress()) + (uintptr_t)entry.replacement.image.offset;
+ break;
+ case closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ newValue = (uintptr_t)_dyldCacheAddress + (uintptr_t)entry.replacement.sharedCache.offset;
+ break;
+ case closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ // this means the symbol was missing in the cache override dylib, so set any uses to NULL
+ newValue = (uintptr_t)entry.replacement.absolute.value;
+ break;
+ default:
+ assert(0 && "bad replacement kind");
+ }
+ lastCachedDylibImage->forEachPatchableUseOfExport(entry.exportCacheOffset, ^(closure::Image::PatchableExport::PatchLocation patchLocation) {
+ uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+ #if __has_feature(ptrauth_calls)
+ if ( patchLocation.authenticated ) {
+ MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+ fixupInfo.authRebase.auth = true;
+ fixupInfo.authRebase.addrDiv = patchLocation.usesAddressDiversity;
+ fixupInfo.authRebase.diversity = patchLocation.discriminator;
+ fixupInfo.authRebase.key = patchLocation.key;
+ *loc = fixupInfo.signPointer(loc, newValue + patchLocation.getAddend());
+ log_fixups("dyld: cache fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+ loc, (void*)*loc, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName());
+ return;
+ }
#endif
+ log_fixups("dyld: cache fixup: *%p = 0x%0lX (dyld cache patch)\n", loc, newValue + (uintptr_t)patchLocation.getAddend());
+ *loc = newValue + (uintptr_t)patchLocation.getAddend();
+ });
+ });
+ if ( suspendedAccounting )
+ Loader::vmAccountingSetSuspended(false, log_fixups);
}
-void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+void AllImages::runStartupInitialzers()
{
- 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;
+ __block bool mainExecutableInitializerNeedsToRun = true;
+ __block uint32_t imageIndex = 0;
+ while ( mainExecutableInitializerNeedsToRun ) {
+ __block const closure::Image* image = nullptr;
+ withReadLock(^{
+ image = _loadedImages[imageIndex].image();
+ if ( _loadedImages[imageIndex].loadedAddress()->isMainExecutable() )
+ mainExecutableInitializerNeedsToRun = false;
+ });
+ runInitialzersBottomUp(image);
+ ++imageIndex;
+ }
+}
+
+
+// Find image in _loadedImages which has ImageNum == num.
+// Try indexHint first, if hint is wrong, updated it, so next use is faster.
+LoadedImage AllImages::findImageNum(closure::ImageNum num, uint32_t& indexHint)
+{
+ __block LoadedImage copy;
+ withReadLock(^{
+ if ( (indexHint >= _loadedImages.count()) || !_loadedImages[indexHint].image()->representsImageNum(num) ) {
+ indexHint = 0;
+ for (indexHint=0; indexHint < _loadedImages.count(); ++indexHint) {
+ if ( _loadedImages[indexHint].image()->representsImageNum(num) )
+ break;
+ }
+ assert(indexHint < _loadedImages.count());
+ }
+ copy = _loadedImages[indexHint];
+ });
+ return copy;
+}
+
+
+// Change the state of the LoadedImage in _loadedImages which has ImageNum == num.
+// Only change state if current state is expectedCurrentState (atomic swap).
+bool AllImages::swapImageState(closure::ImageNum num, uint32_t& indexHint, LoadedImage::State expectedCurrentState, LoadedImage::State newState)
+{
+ __block bool result = false;
+ withWriteLock(^{
+ if ( (indexHint >= _loadedImages.count()) || !_loadedImages[indexHint].image()->representsImageNum(num) ) {
+ indexHint = 0;
+ for (indexHint=0; indexHint < _loadedImages.count(); ++indexHint) {
+ if ( _loadedImages[indexHint].image()->representsImageNum(num) )
+ break;
}
+ assert(indexHint < _loadedImages.count());
}
- if ( !foundInImages ) {
- launch_cache::Image img(imageData);
- log_fixups("did not find loaded image to patch into cache: %s\n", img.path());
+ if ( _loadedImages[indexHint].state() == expectedCurrentState ) {
+ _loadedImages[indexHint].setState(newState);
+ result = true;
}
});
- if ( suspendedAccounting )
- vmAccountingSetSuspended(false);
+ return result;
+}
+
+// dyld3 pre-builds the order initializers need to be run (bottom up) in a list in the closure.
+// This method uses that list to run all initializers.
+// Because an initializer may call dlopen() and/or create threads, the _loadedImages array
+// may move under us. So, never keep a pointer into it. Always reference images by ImageNum
+// and use hint to make that faster in the case where the _loadedImages does not move.
+void AllImages::runInitialzersBottomUp(const closure::Image* topImage)
+{
+ // walk closure specified initializer list, already ordered bottom up
+ topImage->forEachImageToInitBefore(^(closure::ImageNum imageToInit, bool& stop) {
+ // get copy of LoadedImage about imageToInit, but don't keep reference into _loadedImages, because it may move if initialzers call dlopen()
+ uint32_t indexHint = 0;
+ LoadedImage loadedImageCopy = findImageNum(imageToInit, indexHint);
+ // skip if the image is already inited, or in process of being inited (dependency cycle)
+ if ( (loadedImageCopy.state() == LoadedImage::State::fixedUp) && swapImageState(imageToInit, indexHint, LoadedImage::State::fixedUp, LoadedImage::State::beingInited) ) {
+ // tell objc to run any +load methods in image
+ if ( (_objcNotifyInit != nullptr) && loadedImageCopy.image()->mayHavePlusLoads() ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)loadedImageCopy.loadedAddress(), 0, 0);
+ const char* path = imagePath(loadedImageCopy.image());
+ log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", loadedImageCopy.loadedAddress(), path);
+ (*_objcNotifyInit)(path, loadedImageCopy.loadedAddress());
+ }
+
+ // run all initializers in image
+ runAllInitializersInImage(loadedImageCopy.image(), loadedImageCopy.loadedAddress());
+
+ // advance state to inited
+ swapImageState(imageToInit, indexHint, LoadedImage::State::beingInited, LoadedImage::State::inited);
+ }
+ });
+}
+
+
+void AllImages::runLibSystemInitializer(const LoadedImage& libSystem)
+{
+ // run all initializers in libSystem.dylib
+ runAllInitializersInImage(libSystem.image(), libSystem.loadedAddress());
+
+ // Note: during libSystem's initialization, libdyld_initializer() is called which copies _initialImages to _loadedImages
+
+ // mark libSystem.dylib as being inited, so later recursive-init would re-run it
+ for (LoadedImage& li : _loadedImages) {
+ if ( li.loadedAddress() == libSystem.loadedAddress() ) {
+ li.setState(LoadedImage::State::inited);
+ break;
+ }
+ }
}
-void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage)
+void AllImages::runAllInitializersInImage(const closure::Image* image, const MachOLoaded* ml)
{
- // run all initializers in image
- launch_cache::Image libSystemImage(libSystemBinImage);
- libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) {
+ image->forEachInitializer(ml, ^(const void* func) {
Initializer initFunc = (Initializer)func;
- dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+#if __has_feature(ptrauth_calls)
+ initFunc = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)initFunc, 0, 0);
+#endif
+ {
+ ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)ml, (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;
}
+ log_initializers("dyld: called initialzer %p in %s\n", initFunc, image->path());
});
}
-void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress)
+const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool fromOFI, const void* callerAddress)
{
- 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());
+ // quick check if path is in shared cache and already loaded
+ if ( _dyldCacheAddress != nullptr ) {
+ uint32_t dyldCacheImageIndex;
+ if ( _dyldCacheAddress->hasImagePath(path, dyldCacheImageIndex) ) {
+ uint64_t mTime;
+ uint64_t inode;
+ const MachOLoaded* mh = (MachOLoaded*)_dyldCacheAddress->getIndexedImageEntry(dyldCacheImageIndex, mTime, inode);
+ // Note: we do not need readLock because this is within global dlopen lock
+ for (const LoadedImage& li : _loadedImages) {
+ if ( li.loadedAddress() == mh ) {
+ return mh;
}
- // 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);
- });
+ }
+ }
+
+ __block closure::ImageNum callerImageNum = 0;
+ STACK_ALLOC_ARRAY(LoadedImage, loadedList, 1024);
+ for (const LoadedImage& li : _loadedImages) {
+ loadedList.push_back(li);
+ uint8_t permissions;
+ if ( (callerImageNum == 0) && li.image()->containsAddress(callerAddress, li.loadedAddress(), &permissions) ) {
+ callerImageNum = li.image()->imageNum();
+ }
+ //fprintf(stderr, "mh=%p, image=%p, imageNum=0x%04X, path=%s\n", li.loadedAddress(), li.image(), li.image()->imageNum(), li.image()->path());
+ }
+ uintptr_t alreadyLoadedCount = loadedList.count();
+
+ // make closure
+ closure::ImageNum topImageNum = 0;
+ const closure::DlopenClosure* newClosure;
+
+ // First try with closures from the shared cache permitted.
+ // Then try again with forcing a new closure
+ for (bool canUseSharedCacheClosure : { true, false }) {
+ closure::FileSystemPhysical fileSystem;
+ closure::ClosureBuilder::AtPath atPathHanding = (_allowAtPaths ? closure::ClosureBuilder::AtPath::all : closure::ClosureBuilder::AtPath::onlyInRPaths);
+ closure::ClosureBuilder cb(_nextImageNum, fileSystem, _dyldCacheAddress, true, closure::gPathOverrides, atPathHanding);
+ newClosure = cb.makeDlopenClosure(path, _mainClosure, loadedList, callerImageNum, rtldNoLoad, canUseSharedCacheClosure, &topImageNum);
+ if ( newClosure == closure::ClosureBuilder::sRetryDlopenClosure ) {
+ log_apis(" dlopen: closure builder needs to retry: %s\n", path);
+ assert(canUseSharedCacheClosure);
+ continue;
+ }
+ if ( (newClosure == nullptr) && (topImageNum == 0) ) {
+ if ( cb.diagnostics().hasError())
+ diag.error("%s", cb.diagnostics().errorMessage());
+ else if ( !rtldNoLoad )
+ diag.error("dlopen(): file not found: %s", path);
+ return nullptr;
+ }
+ // save off next available ImageNum for use by next call to dlopen()
+ _nextImageNum = cb.nextFreeImageNum();
+ break;
+ }
+
+ if ( newClosure != nullptr ) {
+ // if new closure contains an ImageArray, add it to list
+ if ( const closure::ImageArray* newArray = newClosure->images() ) {
+ appendToImagesArray(newArray);
+ }
+ log_apis(" dlopen: made closure: %p\n", newClosure);
+ }
+
+ // if already loaded, just bump refCount and return
+ if ( (newClosure == nullptr) && (topImageNum != 0) ) {
+ for (LoadedImage& li : _loadedImages) {
+ if ( li.image()->imageNum() == topImageNum ) {
+ // is already loaded
+ const MachOLoaded* topLoadAddress = li.loadedAddress();
+ if ( !li.image()->inDyldCache() )
+ incRefCount(topLoadAddress);
+ log_apis(" dlopen: already loaded as '%s'\n", li.image()->path());
+ // if previously opened with RTLD_LOCAL, but now opened with RTLD_GLOBAL, unhide it
+ if ( !rtldLocal && li.hideFromFlatSearch() )
+ li.setHideFromFlatSearch(false);
+ // if called with RTLD_NODELETE, mark it as never-unload
+ if ( rtldNoDelete )
+ li.markLeaveMapped();
+ return topLoadAddress;
+ }
+ }
+ }
+
+ // run loader to load all new images
+ Loader loader(loadedList, _dyldCacheAddress, imagesArrays(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
+ const closure::Image* topImage = closure::ImageArray::findImage(imagesArrays(), topImageNum);
+ if ( newClosure == nullptr ) {
+ if ( topImageNum < dyld3::closure::kLastDyldCacheImageNum )
+ log_apis(" dlopen: using image in dyld shared cache %p\n", topImage);
+ else
+ log_apis(" dlopen: using pre-built dlopen closure %p\n", topImage);
+ }
+ uintptr_t topIndex = loadedList.count();
+ LoadedImage topLoadedImage = LoadedImage::make(topImage);
+ if ( rtldLocal && !topImage->inDyldCache() )
+ topLoadedImage.setHideFromFlatSearch(true);
+ if ( rtldNoDelete && !topImage->inDyldCache() )
+ topLoadedImage.markLeaveMapped();
+ loader.addImage(topLoadedImage);
+
+
+ // recursively load all dependents and fill in allImages array
+ loader.completeAllDependents(diag, topIndex);
+ if ( diag.hasError() )
+ return nullptr;
+ loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI, topIndex);
+ if ( diag.hasError() )
+ return nullptr;
+
+ const MachOLoaded* topLoadAddress = loadedList[topIndex].loadedAddress();
+
+ // bump dlopen refcount of image directly loaded
+ if ( !topImage->inDyldCache() )
+ incRefCount(topLoadAddress);
+
+ // tell gAllImages about new images
+ const uint32_t newImageCount = (uint32_t)(loadedList.count() - alreadyLoadedCount);
+ addImages(loadedList.subArray(alreadyLoadedCount, newImageCount));
+
+ // if closure adds images that override dyld cache, patch cache
+ if ( newClosure != nullptr )
+ applyInterposingToDyldCache(newClosure);
+
+ runImageNotifiers(loadedList.subArray(alreadyLoadedCount, newImageCount));
+
+ // run initializers
+ runInitialzersBottomUp(topImage);
+
+ return topLoadAddress;
+}
+
+void AllImages::appendToImagesArray(const closure::ImageArray* newArray)
+{
+ _imagesArrays.push_back(newArray);
}
+const Array<const closure::ImageArray*>& AllImages::imagesArrays()
+{
+ return _imagesArrays.array();
+}
+
+bool AllImages::isRestricted() const
+{
+ return !_allowEnvPaths;
+}
+
+
+
} // namespace dyld3
#ifndef __ALL_IMAGES_H__
#define __ALL_IMAGES_H__
-#include <pthread.h>
#include <mach-o/loader.h>
+#include <pthread.h>
+#include <os/lock_private.h>
#include "dyld_priv.h"
-#include "LaunchCache.h"
+#include "Closure.h"
#include "Loading.h"
-
+#include "MachOLoaded.h"
+#include "DyldSharedCache.h"
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// only in macOS and deprecated
-struct VIS_HIDDEN __NSObjectFileImage
+struct VIS_HIDDEN OFIInfo
{
- 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;
+ const char* path; // = nullptr;
+ const void* memSource; // = nullptr;
+ size_t memLength; // = 0;
+ const dyld3::MachOLoaded* loadAddress; // = nullptr;
+ uint64_t imageNum; // = 0;
};
#endif
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);
+ typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+ typedef void (*LoadNotifyFunc)(const mach_header* mh, const char* path, bool unloadable);
+
+ void init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+ const Array<LoadedImage>& initialImages);
+ void setRestrictions(bool allowAtPaths, bool allowEnvPaths);
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();
+ void addImages(const Array<LoadedImage>& newImages);
+ void removeImages(const Array<LoadedImage>& unloadImages);
+ void runImageNotifiers(const Array<LoadedImage>& newImages);
+ void applyInterposingToDyldCache(const closure::Closure* closure);
+ void runStartupInitialzers();
+ void runInitialzersBottomUp(const closure::Image* topImage);
+ void runLibSystemInitializer(const LoadedImage& libSystem);
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;
+
+ void forEachImage(void (^handler)(const LoadedImage& loadedImage, bool& stop)) const;
+ const MachOLoaded* findDependent(const MachOLoaded* mh, uint32_t depIndex);
+ void visitDependentsTopDown(const LoadedImage& start, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
+ void infoForImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
+ bool infoForImageMappedAt(const void* addr, const MachOLoaded** ml, uint64_t* textSize, const char** path) const;
+ void infoForNonCachedImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
+ void infoForImageWithLoadAddress(const MachOLoaded*, void (^handler)(const LoadedImage& foundImage)) const;
+ const char* pathForImageMappedAt(const void* addr) const;
+ const char* imagePathByIndex(uint32_t index) const;
+ const mach_header* imageLoadAddressByIndex(uint32_t index) const;
+ bool immutableMemory(const void* addr, size_t length) const;
+
+ bool isRestricted() const;
+ const MachOLoaded* mainExecutable() const;
+ const closure::Image* mainExecutableImage() const;
const void* cacheLoadAddress() const { return _dyldCacheAddress; }
const char* dyldCachePath() const { return _dyldCachePath; }
- const char* imagePath(const BinaryImage*) const;
+ bool dyldCacheHasPath(const char* path) const;
+ const char* imagePath(const closure::Image*) const;
+ dyld_platform_t platform() 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);
+ const Array<const closure::ImageArray*>& imagesArrays();
- 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 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 addLoadNotifier(LoadNotifyFunc);
- 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);
+ void notifyMonitorLoads(const Array<LoadedImage>& newImages);
+ void notifyMonitorUnloads(const Array<LoadedImage>& unloadingImages);
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- __NSObjectFileImage* addNSObjectFileImage();
- bool hasNSObjectFileImage(__NSObjectFileImage*);
- void removeNSObjectFileImage(__NSObjectFileImage*);
+ NSObjectFileImage addNSObjectFileImage(const OFIInfo&);
+ void removeNSObjectFileImage(NSObjectFileImage);
+ bool forNSObjectFileImage(NSObjectFileImage imageHandle,
+ void (^handler)(OFIInfo& image));
#endif
+ const MachOLoaded* dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool fromOFI, const void* callerAddress);
+
struct ProgramVars
{
const void* mh;
void setProgramVars(ProgramVars* vars);
private:
+ friend class Reaper;
+
+ struct DlopenCount {
+ const mach_header* loadAddress;
+ uintptr_t refCount;
+ };
+
typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
- typedef const launch_cache::DynArray<loader::ImageInfo> StartImageArray;
+ typedef const Array<LoadedImage> StartImageArray;
- void runInitialzersInImage(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+ void runInitialzersInImage(const mach_header* imageLoadAddress, const closure::Image* image);
void mirrorToOldAllImageInfos();
void garbageCollectImages();
- void vmAccountingSetSuspended(bool suspend);
- void copyCurrentGroupsNoLock(ImageGroupList& groups) const;
-
- const BinaryClosure* _mainClosure = nullptr;
- const void* _dyldCacheAddress = nullptr;
+ void breadthFirstRecurseDependents(Array<closure::ImageNum>& visited, const LoadedImage& nodeLi, bool& stop, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
+ void appendToImagesArray(const closure::ImageArray* newArray);
+ void withReadLock(void (^work)()) const;
+ void withWriteLock(void (^work)());
+ void withNotifiersLock(void (^work)()) const;
+ bool findImage(const mach_header* loadAddress, LoadedImage& foundImage) const;
+ bool findImageNum(closure::ImageNum imageNum, LoadedImage& foundImage) const;
+ LoadedImage findImageNum(closure::ImageNum num, uint32_t& indexHint);
+ bool swapImageState(closure::ImageNum num, uint32_t& indexHint, LoadedImage::State expectedCurrentState, LoadedImage::State newState);
+ void runAllInitializersInImage(const closure::Image* image, const MachOLoaded* ml);
+ void recomputeBounds();
+
+ void constructMachPorts(int slot);
+ void teardownMachPorts(int slot);
+ void forEachPortSlot(void (^callback)(int slot));
+ void sendMachMessage(int slot, mach_msg_id_t msg_id, mach_msg_header_t* msg_buffer, mach_msg_size_t msg_size);
+ void notifyMonitoringDyld(bool unloading, const Array<LoadedImage>& images);
+
+ typedef closure::ImageArray ImageArray;
+
+ const closure::LaunchClosure* _mainClosure = nullptr;
+ const DyldSharedCache* _dyldCacheAddress = nullptr;
const char* _dyldCachePath = nullptr;
uint64_t _dyldCacheSlide = 0;
StartImageArray* _initialImages = nullptr;
ProgramVars* _programVars = nullptr;
dyld_all_image_infos* _oldAllImageInfos = nullptr;
dyld_image_info* _oldAllImageArray = nullptr;
+ dyld_platform_t _platform = 0;
uint32_t _oldArrayAllocCount = 0;
- pthread_mutex_t _initializerLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
- pthread_cond_t _initializerCondition= PTHREAD_COND_INITIALIZER;
+ closure::ImageNum _nextImageNum = 0;
int32_t _gcCount = 0;
+ bool _processDOFs = false;
+ bool _allowAtPaths = false;
+ bool _allowEnvPaths = false;
+ uintptr_t _lowestNonCached = 0;
+ uintptr_t _highestNonCached = UINTPTR_MAX;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+ mutable os_unfair_recursive_lock _loadImagesLock = OS_UNFAIR_RECURSIVE_LOCK_INIT;
+ mutable os_unfair_recursive_lock _notifiersLock = OS_UNFAIR_RECURSIVE_LOCK_INIT;
+#else
+ mutable pthread_mutex_t _loadImagesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+ mutable pthread_mutex_t _notifiersLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+#endif
+ GrowableArray<const ImageArray*, 4, 4> _imagesArrays;
+ GrowableArray<NotifyFunc, 4, 4> _loadNotifiers;
+ GrowableArray<NotifyFunc, 4, 4> _unloadNotifiers;
+ GrowableArray<LoadNotifyFunc, 4, 4> _loadNotifiers2;
+ GrowableArray<DlopenCount, 4, 4> _dlopenRefCounts;
+ GrowableArray<LoadedImage, 16> _loadedImages;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ uint64_t _nextObjectFileImageNum = 0;
+ GrowableArray<OFIInfo, 4, 1> _objectFileImages;
+#endif
};
extern AllImages gAllImages;
--- /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 Array_h
+#define Array_h
+
+#include <algorithm>
+#include <stdint.h>
+#include <stddef.h>
+#include <mach/mach.h>
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+
+//
+// Similar to std::vector<> but storage is pre-allocated and cannot be re-allocated.
+// Storage is normally stack allocated.
+//
+// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
+//
+template <typename T>
+class VIS_HIDDEN Array
+{
+public:
+ Array() : _elements(nullptr), _allocCount(0), _usedCount(0) {}
+ Array(T* storage, uintptr_t allocCount, uintptr_t usedCount=0) : _elements(storage), _allocCount(allocCount), _usedCount(usedCount) {}
+ void setInitialStorage(T* storage, uintptr_t allocCount) { assert(_usedCount == 0); _elements=storage; _allocCount=allocCount; }
+
+ T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
+ const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+ T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+ uintptr_t count() const { return _usedCount; }
+ uintptr_t maxCount() const { return _allocCount; }
+ uintptr_t freeCount() const { return _allocCount - _usedCount; }
+ bool empty() const { return (_usedCount == 0); }
+ uintptr_t index(const T& element) { return &element - _elements; }
+ void push_back(const T& t) { assert(_usedCount < _allocCount); _elements[_usedCount++] = t; }
+ void pop_back() { assert(_usedCount > 0); _usedCount--; }
+ T* begin() { return &_elements[0]; }
+ T* end() { return &_elements[_usedCount]; }
+ const T* begin() const { return &_elements[0]; }
+ const T* end() const { return &_elements[_usedCount]; }
+ const Array<T> subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
+ return Array<T>(&_elements[start], size, size); }
+ bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+ void remove(size_t idx) { assert(idx < _usedCount); ::memmove(&_elements[idx], &_elements[idx+1], sizeof(T)*(_usedCount-idx-1)); }
+
+protected:
+ T* _elements;
+ uintptr_t _allocCount;
+ uintptr_t _usedCount;
+};
+
+
+// If an Array<>.setInitialStorage() is used, the array may out live the stack space of the storage.
+// To allow cleanup to be done to array elements when the stack goes away, you can make a local
+// variable of ArrayFinalizer<>.
+template <typename T>
+class VIS_HIDDEN ArrayFinalizer
+{
+public:
+ typedef void (^CleanUp)(T& element);
+ ArrayFinalizer(Array<T>& array, CleanUp handler) : _array(array), _handler(handler) { }
+ ~ArrayFinalizer() { for(T& element : _array) _handler(element); }
+private:
+ Array<T>& _array;
+ CleanUp _handler;
+};
+
+
+
+//
+// Similar to Array<> but if the array overflows, it is re-allocated using vm_allocate().
+// When the variable goes out of scope, any vm_allocate()ed storage is released.
+// if MAXCOUNT is specified, then only one one vm_allocate() to that size is done.
+//
+template <typename T, uintptr_t MAXCOUNT=0xFFFFFFFF>
+class VIS_HIDDEN OverflowSafeArray : public Array<T>
+{
+public:
+ OverflowSafeArray() : Array<T>(nullptr, 0) {}
+ OverflowSafeArray(T* stackStorage, uintptr_t stackAllocCount) : Array<T>(stackStorage, stackAllocCount) {}
+ ~OverflowSafeArray();
+
+ void push_back(const T& t) { verifySpace(1); this->_elements[this->_usedCount++] = t; }
+ void clear() { this->_usedCount = 0; }
+ void reserve(uintptr_t n) { if (this->_allocCount < n) growTo(n); }
+
+protected:
+ void growTo(uintptr_t n);
+ void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+ vm_address_t _overflowBuffer = 0;
+ vm_size_t _overflowBufferSize = 0;
+};
+
+
+template <typename T, uintptr_t MAXCOUNT>
+inline void OverflowSafeArray<T,MAXCOUNT>::growTo(uintptr_t n)
+{
+ vm_address_t oldBuffer = _overflowBuffer;
+ vm_size_t oldBufferSize = _overflowBufferSize;
+ if ( MAXCOUNT != 0xFFFFFFFF ) {
+ assert(oldBufferSize == 0); // only re-alloc once
+ // MAXCOUNT is specified, so immediately jump to that size
+ _overflowBufferSize = round_page(MAXCOUNT * sizeof(T));
+ }
+ else {
+ // MAXCOUNT is not specified, keep doubling size
+ _overflowBufferSize = round_page(std::max(this->_allocCount * 2, n) * sizeof(T));
+ }
+ assert(::vm_allocate(mach_task_self(), &_overflowBuffer, _overflowBufferSize, VM_FLAGS_ANYWHERE) == KERN_SUCCESS);
+ ::memcpy((void*)_overflowBuffer, this->_elements, this->_usedCount*sizeof(T));
+ this->_elements = (T*)_overflowBuffer;
+ this->_allocCount = _overflowBufferSize / sizeof(T);
+
+ if ( oldBuffer != 0 )
+ ::vm_deallocate(mach_task_self(), oldBuffer, oldBufferSize);
+}
+
+template <typename T, uintptr_t MAXCOUNT>
+inline OverflowSafeArray<T,MAXCOUNT>::~OverflowSafeArray()
+{
+ if ( _overflowBuffer != 0 )
+ ::vm_deallocate(mach_task_self(), _overflowBuffer, _overflowBufferSize);
+}
+
+
+
+
+#if BUILDING_LIBDYLD
+//
+// Similar to std::vector<> but storage is initially allocated in the object. But if it needs to
+// grow beyond, it will use malloc. The QUANT template arg is the "quantum" size for allocations.
+// When the allocation needs to be grown, it is re-allocated at the required size rounded up to
+// the next quantum.
+//
+// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
+//
+// Note: this should be a subclass of Array<T> but doing so disables the compiler from optimizing away static constructors
+//
+template <typename T, int QUANT=4, int INIT=1>
+class VIS_HIDDEN GrowableArray
+{
+public:
+
+ T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
+ const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+ T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+ uintptr_t count() const { return _usedCount; }
+ uintptr_t maxCount() const { return _allocCount; }
+ bool empty() const { return (_usedCount == 0); }
+ uintptr_t index(const T& element) { return &element - _elements; }
+ void push_back(const T& t) { verifySpace(1); _elements[_usedCount++] = t; }
+ void append(const Array<T>& a);
+ void pop_back() { assert(_usedCount > 0); _usedCount--; }
+ T* begin() { return &_elements[0]; }
+ T* end() { return &_elements[_usedCount]; }
+ const T* begin() const { return &_elements[0]; }
+ const T* end() const { return &_elements[_usedCount]; }
+ const Array<T> subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
+ return Array<T>(&_elements[start], size, size); }
+ const Array<T>& array() const { return *((Array<T>*)this); }
+ bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+ void erase(T& targ);
+
+protected:
+ void growTo(uintptr_t n);
+ void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+ T* _elements = _initialAlloc;
+ uintptr_t _allocCount = INIT;
+ uintptr_t _usedCount = 0;
+ T _initialAlloc[INIT] = { };
+};
+
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
+{
+ uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
+ T* newArray = (T*)::malloc(sizeof(T)*newCount);
+ T* oldArray = this->_elements;
+ if ( this->_usedCount != 0 )
+ ::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
+ this->_elements = newArray;
+ this->_allocCount = newCount;
+ if ( oldArray != this->_initialAlloc )
+ ::free(oldArray);
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::append(const Array<T>& a)
+{
+ verifySpace(a.count());
+ ::memcpy(&_elements[_usedCount], a.begin(), a.count()*sizeof(T));
+ _usedCount += a.count();
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
+{
+ intptr_t index = &targ - _elements;
+ assert(index >= 0);
+ assert(index < (intptr_t)_usedCount);
+ intptr_t moveCount = _usedCount-index-1;
+ if ( moveCount > 0 )
+ ::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
+ _usedCount -= 1;
+}
+
+#endif // BUILDING_LIBDYLD
+
+
+
+// STACK_ALLOC_ARRAY(foo, myarray, 10);
+// myarray is of type Array<foo>
+#define STACK_ALLOC_ARRAY(_type, _name, _count) \
+ uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+ __block dyld3::Array<_type> _name((_type*)__##_name##_array_alloc, _count);
+
+
+// STACK_ALLOC_OVERFLOW_SAFE_ARRAY(foo, myarray, 10);
+// myarray is of type OverflowSafeArray<foo>
+#define STACK_ALLOC_OVERFLOW_SAFE_ARRAY(_type, _name, _count) \
+ uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+ __block dyld3::OverflowSafeArray<_type> _name((_type*)__##_name##_array_alloc, _count);
+
+
+// work around compiler bug where:
+// __block type name[count];
+// is not accessible in a block
+#define BLOCK_ACCCESSIBLE_ARRAY(_type, _name, _count) \
+ _type __##_name##_array_alloc[_count]; \
+ _type* _name = __##_name##_array_alloc;
+
+
+} // namespace dyld3
+
+#endif /* Array_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 <assert.h>
+#include <uuid/uuid.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "Closure.h"
+#include "MachOFile.h"
+#include "MachOLoaded.h"
+
+
+namespace dyld {
+ extern void log(const char* format, ...) __attribute__((format(printf, 1, 2)));
+}
+
+namespace dyld3 {
+namespace closure {
+
+
+//////////////////////////// TypedBytes ////////////////////////////////////////
+
+const void* TypedBytes::payload() const
+{
+ return (uint8_t*)this + sizeof(TypedBytes);
+}
+
+void* TypedBytes::payload()
+{
+ return (uint8_t*)this + sizeof(TypedBytes);
+}
+
+
+//////////////////////////// ContainerTypedBytes ////////////////////////////////////////
+
+const TypedBytes* ContainerTypedBytes::first() const
+{
+ return (TypedBytes*)payload();
+}
+
+const TypedBytes* ContainerTypedBytes::next(const TypedBytes* p) const
+{
+ assert((p->payloadLength & 0x3) == 0);
+ return (TypedBytes*)((uint8_t*)(p->payload()) + p->payloadLength);
+}
+
+void ContainerTypedBytes::forEachAttribute(void (^handler)(const TypedBytes* typedBytes, bool& stop)) const
+{
+ assert(((long)this & 0x3) == 0);
+ const TypedBytes* end = next(this);
+ bool stop = false;
+ for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
+ handler(p, stop);
+ }
+}
+
+void ContainerTypedBytes::forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const
+{
+ forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+ if ( (Type)(typedBytes->type) != requestedType )
+ return;
+ handler(typedBytes->payload(), typedBytes->payloadLength, stop);
+ });
+}
+
+const void* ContainerTypedBytes::findAttributePayload(Type requestedType, uint32_t* payloadSize) const
+{
+ assert(((long)this & 0x3) == 0);
+ if ( payloadSize != nullptr )
+ *payloadSize = 0;
+ const TypedBytes* end = next(this);
+ bool stop = false;
+ for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
+ if ( (Type)(p->type) == requestedType ) {
+ if ( payloadSize != nullptr )
+ *payloadSize = p->payloadLength;
+ return p->payload();
+ }
+ }
+ return nullptr;
+}
+
+
+//////////////////////////// Image ////////////////////////////////////////
+
+const Image::Flags& Image::getFlags() const
+{
+ return *(Flags*)((uint8_t*)this + 2*sizeof(TypedBytes));
+}
+
+bool Image::isInvalid() const
+{
+ return getFlags().isInvalid;
+}
+
+size_t Image::size() const
+{
+ return sizeof(TypedBytes) + this->payloadLength;
+}
+
+ImageNum Image::imageNum() const
+{
+ return getFlags().imageNum;
+}
+
+// returns true iff 'num' is this image's ImageNum, or this image overrides that imageNum (in dyld cache)
+bool Image::representsImageNum(ImageNum num) const
+{
+ const Flags& flags = getFlags();
+ if ( flags.imageNum == num )
+ return true;
+ if ( !flags.isDylib )
+ return false;
+ if ( flags.inDyldCache )
+ return false;
+ ImageNum cacheImageNum;
+ if ( isOverrideOfDyldCacheImage(cacheImageNum) )
+ return (cacheImageNum == num);
+ return false;
+}
+
+uint32_t Image::maxLoadCount() const
+{
+ return getFlags().maxLoadCount;
+}
+
+bool Image::isBundle() const
+{
+ return getFlags().isBundle;
+}
+
+bool Image::isDylib() const
+{
+ return getFlags().isDylib;
+}
+
+bool Image::isExecutable() const
+{
+ return getFlags().isExecutable;
+}
+
+bool Image::hasObjC() const
+{
+ return getFlags().hasObjC;
+}
+
+bool Image::is64() const
+{
+ return getFlags().is64;
+}
+
+bool Image::hasWeakDefs() const
+{
+ return getFlags().hasWeakDefs;
+}
+
+bool Image::mayHavePlusLoads() const
+{
+ return getFlags().mayHavePlusLoads;
+}
+
+bool Image::neverUnload() const
+{
+ return getFlags().neverUnload;
+}
+
+bool Image::overridableDylib() const
+{
+ return getFlags().overridableDylib;
+}
+
+bool Image::inDyldCache() const
+{
+ return getFlags().inDyldCache;
+}
+
+const char* Image::path() const
+{
+ // might be multiple pathWithHash enties, first is canonical name
+ const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash);
+ assert(result && "Image missing pathWithHash");
+ return result->path;
+}
+
+const char* Image::leafName() const
+{
+ uint32_t size;
+ // might be multiple pathWithHash enties, first is canonical name
+ const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash, &size);
+ assert(result && "Image missing pathWithHash");
+ for (const char* p=(char*)result + size; p > result->path; --p) {
+ if ( *p == '/' )
+ return p+1;
+ }
+ return result->path;
+}
+
+bool Image::hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const
+{
+ uint32_t size;
+ const FileInfo* info = (FileInfo*)(findAttributePayload(Type::fileInodeAndTime, &size));
+ if ( info != nullptr ) {
+ assert(size == sizeof(FileInfo));
+ inode = info->inode;
+ mTime = info->modTime;
+ return true;
+ }
+ return false;
+}
+
+bool Image::hasCdHash(uint8_t cdHash[20]) const
+{
+ uint32_t size;
+ const uint8_t* bytes = (uint8_t*)(findAttributePayload(Type::cdHash, &size));
+ if ( bytes != nullptr ) {
+ assert(size == 20);
+ memcpy(cdHash, bytes, 20);
+ return true;
+ }
+ return false;
+}
+
+bool Image::getUuid(uuid_t uuid) const
+{
+ uint32_t size;
+ const uint8_t* bytes = (uint8_t*)(findAttributePayload(Type::uuid, &size));
+ if ( bytes == nullptr )
+ return false;
+ assert(size == 16);
+ memcpy(uuid, bytes, 16);
+ return true;
+}
+
+bool Image::hasCodeSignature(uint32_t& sigFileOffset, uint32_t& sigSize) const
+{
+ uint32_t sz;
+ const Image::CodeSignatureLocation* sigInfo = (Image::CodeSignatureLocation*)(findAttributePayload(Type::codeSignLoc, &sz));
+ if ( sigInfo != nullptr ) {
+ assert(sz == sizeof(Image::CodeSignatureLocation));
+ sigFileOffset = sigInfo->fileOffset;
+ sigSize = sigInfo->fileSize;
+ return true;
+ }
+ return false;
+}
+
+bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+ uint32_t sz;
+ const Image::FairPlayRange* fpInfo = (Image::FairPlayRange*)(findAttributePayload(Type::fairPlayLoc, &sz));
+ if ( fpInfo != nullptr ) {
+ assert(sz == sizeof(Image::FairPlayRange));
+ textOffset = fpInfo->textStartPage * pageSize();
+ size = fpInfo->textPageCount * pageSize();
+ return true;
+ }
+ return false;
+}
+
+const Array<Image::LinkedImage> Image::dependentsArray() const
+{
+ uint32_t size;
+ LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+ assert((size % sizeof(LinkedImage)) == 0);
+ uintptr_t count = size / sizeof(LinkedImage);
+ return Array<Image::LinkedImage>(dependents, count, count);
+}
+
+void Image::forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const
+{
+ uint32_t size;
+ const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+ assert((size % sizeof(LinkedImage)) == 0);
+ const uint32_t count = size / sizeof(LinkedImage);
+ bool stop = false;
+ for (uint32_t i=0; (i < count) && !stop; ++i) {
+ LinkKind kind = dependents[i].kind();
+ ImageNum imageNum = dependents[i].imageNum();
+ // ignore missing weak links
+ if ( (imageNum == kMissingWeakLinkedImage) && (kind == LinkKind::weak) )
+ continue;
+ handler(i, kind, imageNum, stop);
+ }
+}
+
+ImageNum Image::dependentImageNum(uint32_t depIndex) const
+{
+ uint32_t size;
+ const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+ assert((size % sizeof(LinkedImage)) == 0);
+ const uint32_t count = size / sizeof(LinkedImage);
+ assert(depIndex < count);
+ return dependents[depIndex].imageNum();
+}
+
+
+uint32_t Image::hashFunction(const char* str)
+{
+ uint32_t h = 0;
+ for (const char* s=str; *s != '\0'; ++s)
+ h = h*5 + *s;
+ return h;
+}
+
+void Image::forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const
+{
+ __block bool foundFirst = false;
+ forEachAttribute(^(const TypedBytes* typedBytes, bool& stopLoop) {
+ if ( (Type)(typedBytes->type) != Type::pathWithHash )
+ return;
+ if ( foundFirst ) {
+ const PathAndHash* aliasInfo = (PathAndHash*)typedBytes->payload();
+ handler(aliasInfo->path, stopLoop);
+ }
+ else {
+ foundFirst = true;
+ }
+ });
+}
+
+bool Image::hasPathWithHash(const char* path, uint32_t hash) const
+{
+ __block bool found = false;
+ forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+ if ( (Type)(typedBytes->type) != Type::pathWithHash )
+ return;
+ const PathAndHash* pathInfo = (PathAndHash*)typedBytes->payload();
+ if ( (pathInfo->hash == hash) && (strcmp(path, pathInfo->path) == 0) ) {
+ stop = true;
+ found = true;
+ }
+ });
+ return found;
+}
+
+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
+{
+ uint32_t size;
+ const DiskSegment* segments = (DiskSegment*)findAttributePayload(Type::diskSegment, &size);
+ assert(segments != nullptr);
+ assert((size % sizeof(DiskSegment)) == 0);
+ const uint32_t count = size / sizeof(DiskSegment);
+ const uint32_t pageSz = pageSize();
+ 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 < count; ++i) {
+ const DiskSegment* seg = &segments[i];
+ if ( seg->filePageCount != 0 ) {
+ break;
+ }
+ vmOffset -= (uint64_t)seg->vmPageCount * pageSz;
+ }
+ // walk each segment and call handler
+ bool stop = false;
+ for (uint32_t i=0; i < count && !stop; ++i) {
+ const DiskSegment* seg = &segments[i];
+ uint64_t vmSize = (uint64_t)seg->vmPageCount * pageSz;
+ uint32_t fileSize = seg->filePageCount * pageSz;
+ if ( !seg->paddingNotSeg ) {
+ handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop);
+ ++segIndex;
+ }
+ vmOffset += vmSize;
+ fileOffset += fileSize;
+ }
+}
+
+uint32_t Image::pageSize() const
+{
+ if ( getFlags().has16KBpages )
+ return 0x4000;
+ else
+ return 0x1000;
+}
+
+uint32_t Image::cacheOffset() const
+{
+ uint32_t size;
+ const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
+ assert(segments != nullptr);
+ assert((size % sizeof(DyldCacheSegment)) == 0);
+ return segments[0].cacheOffset;
+}
+
+void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+ uint32_t size;
+ const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
+ assert(segments != nullptr);
+ assert((size % sizeof(DyldCacheSegment)) == 0);
+ const uint32_t count = size / sizeof(DyldCacheSegment);
+ bool stop = false;
+ for (uint32_t i=0; i < count; ++i) {
+ uint64_t vmOffset = segments[i].cacheOffset - segments[0].cacheOffset;
+ uint64_t vmSize = segments[i].size;
+ uint8_t permissions = segments[i].permissions;
+ handler(i, vmOffset, vmSize, permissions, stop);
+ if ( stop )
+ break;
+ }
+}
+
+uint64_t Image::textSize() const
+{
+ __block uint64_t result = 0;
+ if ( inDyldCache() ) {
+ forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ result = vmSize;
+ stop = true;
+ });
+ }
+ else {
+ forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ if ( permissions != 0) {
+ result = vmSize;
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permsResult) const
+{
+ __block bool result = false;
+ uint64_t targetAddr = (uint64_t)addr;
+ uint64_t imageStart = (uint64_t)imageLoadAddress;
+ if ( inDyldCache() ) {
+ forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
+ result = true;
+ if ( permsResult )
+ *permsResult = permissions;
+ stop = true;
+ }
+ });
+ }
+ else {
+ forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
+ result = true;
+ if ( permsResult )
+ *permsResult = permissions;
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+uint64_t Image::vmSizeToMap() const
+{
+ uint32_t size;
+ const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
+ assert(info != nullptr);
+ assert(size == sizeof(Image::MappingInfo));
+ return info->totalVmPages * pageSize();
+}
+
+uint64_t Image::sliceOffsetInFile() const
+{
+ uint32_t size;
+ const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
+ assert(info != nullptr);
+ assert(size == sizeof(Image::MappingInfo));
+ return info->sliceOffsetIn4K * 0x1000;
+}
+
+void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
+{
+ uint32_t size;
+ const uint32_t* inits = (uint32_t*)findAttributePayload(Type::initOffsets, &size);
+ if ( inits != nullptr ) {
+ assert((size % sizeof(uint32_t)) == 0);
+ const uint32_t count = size / sizeof(uint32_t);
+ for (uint32_t i=0; i < count; ++i) {
+ uint32_t offset = inits[i];
+ const void* init = (void*)((uint8_t*)imageLoadAddress + offset);
+ handler(init);
+ }
+ }
+}
+
+bool Image::hasInitializers() const
+{
+ uint32_t size;
+ return ( findAttributePayload(Type::initOffsets, &size) != nullptr );
+}
+
+void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* dofSection)) const
+{
+ uint32_t size;
+ const uint32_t* dofs = (uint32_t*)findAttributePayload(Type::dofOffsets, &size);
+ if ( dofs != nullptr ) {
+ assert((size % sizeof(uint32_t)) == 0);
+ const uint32_t count = size / sizeof(uint32_t);
+ for (uint32_t i=0; i < count; ++i) {
+ uint32_t offset = dofs[i];
+ const void* sect = (void*)((uint8_t*)imageLoadAddress + offset);
+ handler(sect);
+ }
+ }
+}
+
+void Image::forEachPatchableExport(void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const
+{
+ forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+ const Image::PatchableExport* pe = (Image::PatchableExport*)payload;
+ assert(size > (sizeof(Image::PatchableExport) + pe->patchLocationsCount*sizeof(PatchableExport::PatchLocation)));
+ handler(pe->cacheOffsetOfImpl, (char*)(&pe->patchLocations[pe->patchLocationsCount]));
+ });
+}
+
+void Image::forEachPatchableUseOfExport(uint32_t cacheOffsetOfImpl, void (^handler)(PatchableExport::PatchLocation patchLocation)) const
+{
+ forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+ const Image::PatchableExport* pe = (Image::PatchableExport*)payload;
+ assert(size > (sizeof(Image::PatchableExport) + pe->patchLocationsCount*sizeof(PatchableExport::PatchLocation)));
+ if ( pe->cacheOffsetOfImpl != cacheOffsetOfImpl )
+ return;
+ const PatchableExport::PatchLocation* start = pe->patchLocations;
+ const PatchableExport::PatchLocation* end = &start[pe->patchLocationsCount];
+ for (const PatchableExport::PatchLocation* p=start; p < end; ++p)
+ handler(*p);
+ });
+}
+
+uint32_t Image::patchableExportCount() const
+{
+ __block uint32_t count = 0;
+ forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+ ++count;
+ });
+ return count;
+}
+
+void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
+ void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
+ void (^chainedFixupsStart)(uint64_t imageOffsetStart, const Array<ResolvedSymbolTarget>& targets, bool& stop)) const
+{
+ const uint32_t pointerSize = is64() ? 8 : 4;
+ uint64_t curRebaseOffset = 0;
+ bool stop = false;
+ for (const Image::RebasePattern& rebasePat : rebaseFixups()) {
+ //fprintf(stderr, " repeat=0x%04X, contig=%d, skip=%d\n", rebasePat.repeatCount, rebasePat.contigCount, rebasePat.skipCount);
+ if ( rebasePat.contigCount == 0 ) {
+ // note: contigCount==0 means this just advances location
+ if ( (rebasePat.repeatCount == 0) && (rebasePat.skipCount == 0) ) {
+ // all zeros is special pattern that means reset to rebase offset to zero
+ curRebaseOffset = 0;
+ }
+ else {
+ curRebaseOffset += rebasePat.repeatCount * rebasePat.skipCount;
+ }
+ }
+ else {
+ for (int r=0; r < rebasePat.repeatCount && !stop; ++r) {
+ for (int i=0; i < rebasePat.contigCount && !stop; ++i) {
+ //fprintf(stderr, " 0x%08llX\n", curRebaseOffset);
+ rebase(curRebaseOffset, stop);
+ curRebaseOffset += pointerSize;
+ }
+ curRebaseOffset += pointerSize * rebasePat.skipCount;
+ }
+ }
+ if ( stop )
+ break;
+ }
+ if ( stop )
+ return;
+
+ for (const Image::BindPattern& bindPat : bindFixups()) {
+ uint64_t curBindOffset = bindPat.startVmOffset;
+ for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
+ bind(curBindOffset, bindPat.target, stop);
+ curBindOffset += (pointerSize * (1 + bindPat.skipCount));
+ if ( stop )
+ break;
+ }
+ if ( stop )
+ break;
+ }
+
+ const Array<Image::ResolvedSymbolTarget> targetsArray = chainedTargets();
+ for (uint64_t start : chainedStarts()) {
+ chainedFixupsStart(start, targetsArray, stop);
+ if ( stop )
+ break;
+ }
+}
+
+void Image::forEachChainedFixup(void* imageLoadAddress, uint64_t imageOffsetChainStart, void (^callback)(uint64_t* fixUpLoc, ChainedFixupPointerOnDisk fixupInfo, bool& stop))
+{
+ bool stop = false;
+ uint64_t* fixupLoc = (uint64_t*)((uint8_t*)imageLoadAddress + imageOffsetChainStart);
+ do {
+ // save off current entry as it will be overwritten in callback
+ ChainedFixupPointerOnDisk info = *((ChainedFixupPointerOnDisk*)fixupLoc);
+ callback(fixupLoc, info, stop);
+ if ( info.plainRebase.next != 0 )
+ fixupLoc += info.plainRebase.next;
+ else
+ stop = true;
+ } while (!stop);
+}
+
+void Image::forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
+ void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
+{
+ bool stop = false;
+ const Array<Image::TextFixupPattern> f = textFixups();
+ for (const Image::TextFixupPattern& pat : f) {
+ uint32_t curOffset = pat.startVmOffset;
+ for (uint16_t i=0; i < pat.repeatCount; ++i) {
+ if ( pat.target.raw == 0 )
+ rebase(curOffset, stop);
+ else
+ bind(curOffset, pat.target, stop);
+ curOffset += pat.skipCount;
+ }
+ }
+}
+
+const Array<Image::RebasePattern> Image::rebaseFixups() const
+{
+ uint32_t rebaseFixupsSize;
+ Image::RebasePattern* rebaseFixupsContent = (RebasePattern*)findAttributePayload(Type::rebaseFixups, &rebaseFixupsSize);
+ uint32_t rebaseCount = rebaseFixupsSize/sizeof(RebasePattern);
+ return Array<RebasePattern>(rebaseFixupsContent, rebaseCount, rebaseCount);
+}
+
+const Array<Image::BindPattern> Image::bindFixups() const
+{
+ uint32_t bindFixupsSize;
+ BindPattern* bindFixupsContent = (BindPattern*)findAttributePayload(Type::bindFixups, &bindFixupsSize);
+ uint32_t bindCount = bindFixupsSize/sizeof(BindPattern);
+ return Array<BindPattern>(bindFixupsContent, bindCount, bindCount);
+}
+
+const Array<uint64_t> Image::chainedStarts() const
+{
+ uint32_t startsSize;
+ uint64_t* starts = (uint64_t*)findAttributePayload(Type::chainedFixupsStarts, &startsSize);
+ uint32_t count = startsSize/sizeof(uint64_t);
+ return Array<uint64_t>(starts, count, count);
+}
+
+const Array<Image::ResolvedSymbolTarget> Image::chainedTargets() const
+{
+ uint32_t size;
+ ResolvedSymbolTarget* targetsContent = (ResolvedSymbolTarget*)findAttributePayload(Type::chainedFixupsTargets, &size);
+ uint32_t count = size/sizeof(ResolvedSymbolTarget);
+ return Array<ResolvedSymbolTarget>(targetsContent, count, count);
+}
+
+const Array<Image::TextFixupPattern> Image::textFixups() const
+{
+ uint32_t fixupsSize;
+ TextFixupPattern* fixupsContent = (TextFixupPattern*)findAttributePayload(Type::textFixups, &fixupsSize);
+ uint32_t count = fixupsSize/sizeof(TextFixupPattern);
+ return Array<TextFixupPattern>(fixupsContent, count, count);
+}
+
+bool Image::isOverrideOfDyldCacheImage(ImageNum& imageNum) const
+{
+ uint32_t size;
+ const uint32_t* content = (uint32_t*)findAttributePayload(Type::imageOverride, &size);
+ if ( content != nullptr ) {
+ assert(size == sizeof(uint32_t));
+ imageNum = *content;
+ return true;
+ }
+ return false;
+}
+
+void Image::forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const
+{
+ uint32_t size;
+ const ImageNum* initBefores = (ImageNum*)findAttributePayload(Type::initBefores, &size);
+ if ( initBefores != nullptr ) {
+ assert((size % sizeof(ImageNum)) == 0);
+ const uint32_t count = size / sizeof(ImageNum);
+ bool stop = false;
+ for (uint32_t i=0; (i < count) && !stop; ++i) {
+ handler(initBefores[i], stop);
+ }
+ }
+}
+
+const char* Image::PatchableExport::PatchLocation::keyName() const
+{
+ return MachOLoaded::ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+Image::PatchableExport::PatchLocation::PatchLocation(size_t cacheOff, uint64_t ad)
+ : cacheOffset(cacheOff), addend(ad), authenticated(0), usesAddressDiversity(0), key(0), discriminator(0)
+{
+ int64_t signedAddend = (int64_t)ad;
+ assert(((signedAddend << 52) >> 52) == signedAddend);
+}
+
+Image::PatchableExport::PatchLocation::PatchLocation(size_t cacheOff, uint64_t ad, dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo)
+ : cacheOffset(cacheOff), addend(ad), authenticated(authInfo.authBind.auth), usesAddressDiversity(authInfo.authBind.addrDiv), key(authInfo.authBind.key), discriminator(authInfo.authBind.diversity)
+{
+ int64_t signedAddend = (int64_t)ad;
+ assert(((signedAddend << 52) >> 52) == signedAddend);
+}
+
+//////////////////////////// ImageArray ////////////////////////////////////////
+
+size_t ImageArray::size() const
+{
+ return sizeof(TypedBytes) + this->payloadLength;
+}
+
+size_t ImageArray::startImageNum() const
+{
+ return firstImageNum;
+}
+
+uint32_t ImageArray::imageCount() const
+{
+ return count;
+}
+
+void ImageArray::forEachImage(void (^callback)(const Image* image, bool& stop)) const
+{
+ bool stop = false;
+ for (uint32_t i=0; i < count && !stop; ++i) {
+ const Image* image = (Image*)((uint8_t*)payload() + offsets[i]);
+ callback(image, stop);
+ if (stop)
+ break;
+ }
+}
+
+bool ImageArray::hasPath(const char* path, ImageNum& num) const
+{
+ const uint32_t hash = Image::hashFunction(path);
+ __block bool found = false;
+ forEachImage(^(const Image* image, bool& stop) {
+ if ( image->hasPathWithHash(path, hash) ) {
+ num = image->imageNum();
+ found = true;
+ stop = true;
+ }
+ });
+ return found;
+}
+
+const Image* ImageArray::imageForNum(ImageNum num) const
+{
+ if ( num < firstImageNum )
+ return nullptr;
+
+ uint32_t index = num - firstImageNum;
+ if ( index >= count )
+ return nullptr;
+
+ return (Image*)((uint8_t*)payload() + offsets[index]);
+}
+
+const Image* ImageArray::findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum)
+{
+ for (const ImageArray* ia : imagesArrays) {
+ if ( const Image* result = ia->imageForNum(imageNum) )
+ return result;
+ }
+ return nullptr;
+}
+
+//////////////////////////// Closure ////////////////////////////////////////
+
+size_t Closure::size() const
+{
+ return sizeof(TypedBytes) + this->payloadLength;
+}
+
+const ImageArray* Closure::images() const
+{
+ __block const TypedBytes* result = nullptr;
+ forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+ if ( (Type)(typedBytes->type) == Type::imageArray ) {
+ result = typedBytes;
+ stop = true;
+ }
+ });
+
+ return (ImageArray*)result;
+}
+
+ImageNum Closure::topImage() const
+{
+ uint32_t size;
+ const ImageNum* top = (ImageNum*)findAttributePayload(Type::topImage, &size);
+ assert(top != nullptr);
+ assert(size == sizeof(ImageNum));
+ return *top;
+}
+
+void Closure::forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const
+{
+ forEachAttributePayload(Type::cacheOverrides, ^(const void* payload, uint32_t size, bool& stop) {
+ assert((size % sizeof(Closure::PatchEntry)) == 0);
+ const PatchEntry* patches = (PatchEntry*)payload;
+ const PatchEntry* patchesEnd = (PatchEntry*)((char*)payload + size);
+ for (const PatchEntry* p=patches; p < patchesEnd; ++p)
+ handler(*p);
+ });
+}
+
+void Closure::deallocate() const
+{
+ ::vm_deallocate(mach_task_self(), (long)this, size());
+}
+
+//////////////////////////// LaunchClosure ////////////////////////////////////////
+
+void LaunchClosure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
+{
+ uint32_t size;
+ const char* paths = (const char*)findAttributePayload(Type::missingFiles, &size);
+ bool stop = false;
+ for (const char* s=paths; s < &paths[size]; ++s) {
+ if ( *s != '\0' )
+ handler(s, stop);
+ if ( stop )
+ break;
+ s += strlen(s);
+ }
+}
+
+bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID) const
+{
+ uint32_t size;
+ const uint8_t* uuidBytes = (uint8_t*)findAttributePayload(Type::dyldCacheUUID, &size);
+ if ( uuidBytes == nullptr )
+ return false;
+ assert(size == sizeof(uuid_t));
+ memcpy(cacheUUID, uuidBytes, sizeof(uuid_t));
+ return true;
+}
+
+const char* LaunchClosure::bootUUID() const
+{
+ uint32_t size;
+ return (char*)findAttributePayload(Type::bootUUID, &size);
+}
+
+void LaunchClosure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
+{
+ forEachAttributePayload(Type::envVar, ^(const void* payload, uint32_t size, bool& stop) {
+ handler((char*)payload, stop);
+ });
+}
+
+ImageNum LaunchClosure::libSystemImageNum() const
+{
+ uint32_t size;
+ const ImageNum* num = (ImageNum*)findAttributePayload(Type::libSystemNum, &size);
+ assert(num != nullptr);
+ assert(size == sizeof(ImageNum));
+ return *num;
+}
+
+void LaunchClosure::libDyldEntry(Image::ResolvedSymbolTarget& loc) const
+{
+ uint32_t size;
+ const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::libDyldEntry, &size);
+ assert(data != nullptr);
+ assert(size == sizeof(Image::ResolvedSymbolTarget));
+ loc = *data;
+}
+
+bool LaunchClosure::mainEntry(Image::ResolvedSymbolTarget& mainLoc) const
+{
+ uint32_t size;
+ const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::mainEntry, &size);
+ if ( data == nullptr )
+ return false;
+ assert(size == sizeof(Image::ResolvedSymbolTarget));
+ mainLoc = *data;
+ return true;
+}
+
+bool LaunchClosure::startEntry(Image::ResolvedSymbolTarget& startLoc) const
+{
+ uint32_t size;
+ const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::startEntry, &size);
+ if ( data == nullptr )
+ return false;
+ assert(size == sizeof(Image::ResolvedSymbolTarget));
+ startLoc = *data;
+ return true;
+}
+
+const LaunchClosure::Flags& LaunchClosure::getFlags() const
+{
+ uint32_t size;
+ const Flags* flags = (Flags*)findAttributePayload(Type::closureFlags, &size);
+ assert(flags != nullptr && "Closure missing Flags");
+ return *flags;
+}
+
+uint32_t LaunchClosure::initialLoadCount() const
+{
+ return getFlags().initImageCount;
+}
+
+bool LaunchClosure::usedAtPaths() const
+{
+ return getFlags().usedAtPaths;
+}
+
+bool LaunchClosure::usedFallbackPaths() const
+{
+ return getFlags().usedFallbackPaths;
+}
+
+void LaunchClosure::forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const
+{
+ forEachAttributePayload(Type::interposeTuples, ^(const void* payload, uint32_t size, bool& stop) {
+ assert((size % sizeof(InterposingTuple)) == 0);
+ uintptr_t count = size / sizeof(InterposingTuple);
+ const InterposingTuple* tuples = (InterposingTuple*)payload;
+ for (uint32_t i=0; i < count && !stop; ++i) {
+ handler(tuples[i], stop);
+ }
+ });
+}
+
+
+
+} // namespace closure
+} // 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 Closures_h
+#define Closures_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#include "Diagnostics.h"
+#include "Array.h"
+#include "MachOLoaded.h"
+#include "SupportedArchs.h"
+
+namespace dyld3 {
+namespace closure {
+
+
+
+// bump this number each time binary format changes
+enum { kFormatVersion = 10 };
+
+
+typedef uint32_t ImageNum;
+
+const ImageNum kFirstDyldCacheImageNum = 0x00000001;
+const ImageNum kLastDyldCacheImageNum = 0x00000FFF;
+const ImageNum kFirstOtherOSImageNum = 0x00001001;
+const ImageNum kLastOtherOSImageNum = 0x00001FFF;
+const ImageNum kFirstLaunchClosureImageNum = 0x00002000;
+const ImageNum kMissingWeakLinkedImage = 0x0FFFFFFF;
+
+
+//
+// Generic typed range of bytes (similar to load commands)
+// Must be 4-byte aligned
+//
+struct VIS_HIDDEN TypedBytes
+{
+ uint32_t type : 8,
+ payloadLength : 24;
+
+ enum class Type {
+ // containers which have an overall length and TypedBytes inside their content
+ launchClosure = 1, // contains TypedBytes of closure attributes including imageArray
+ imageArray = 2, // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
+ image = 3, // contains TypedBytes of image attributes
+ dlopenClosure = 4, // contains TypedBytes of closure attributes including imageArray
+
+ // attributes for Images
+ imageFlags = 7, // sizeof(Image::Flags)
+ pathWithHash = 8, // len = uint32_t + length path + 1, use multiple entries for aliases
+ fileInodeAndTime = 9, // sizeof(FileInfo)
+ cdHash = 10, // 20
+ uuid = 11, // 16
+ mappingInfo = 12, // sizeof(MappingInfo)
+ diskSegment = 13, // sizeof(DiskSegment) * count
+ cacheSegment = 14, // sizeof(DyldCacheSegment) * count
+ dependents = 15, // sizeof(LinkedImage) * count
+ initOffsets = 16, // sizeof(uint32_t) * count
+ dofOffsets = 17, // sizeof(uint32_t) * count
+ codeSignLoc = 18, // sizeof(CodeSignatureLocation)
+ fairPlayLoc = 19, // sizeof(FairPlayRange)
+ rebaseFixups = 20, // sizeof(RebasePattern) * count
+ bindFixups = 21, // sizeof(BindPattern) * count
+ cachePatchInfo = 22, // sizeof(PatchableExport) + count*sizeof(PatchLocation) + strlen(name) // only in dyld cache Images
+ textFixups = 23, // sizeof(TextFixupPattern) * count
+ imageOverride = 24, // sizeof(ImageNum)
+ initBefores = 25, // sizeof(ImageNum) * count
+ chainedFixupsStarts = 26, // sizeof(uint64_t) * count
+ chainedFixupsTargets = 27, // sizeof(ResolvedSymbolTarget) * count
+
+ // attributes for Closures (launch or dlopen)
+ closureFlags = 32, // sizeof(Closure::Flags)
+ dyldCacheUUID = 33, // 16
+ missingFiles = 34,
+ envVar = 35, // "DYLD_BLAH=stuff"
+ topImage = 36, // sizeof(ImageNum)
+ libDyldEntry = 37, // sizeof(ResolvedSymbolTarget)
+ libSystemNum = 38, // sizeof(ImageNum)
+ bootUUID = 39, // c-string 40
+ mainEntry = 40, // sizeof(ResolvedSymbolTarget)
+ startEntry = 41, // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
+ cacheOverrides = 42, // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
+ interposeTuples = 43, // sizeof(InterposingTuple) * count
+ };
+
+ const void* payload() const;
+ void* payload();
+};
+
+
+//
+// A TypedBytes which is a bag of other TypedBytes
+//
+struct VIS_HIDDEN ContainerTypedBytes : TypedBytes
+{
+ void forEachAttribute(void (^callback)(const TypedBytes* typedBytes, bool& stop)) const;
+ void forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const;
+ const void* findAttributePayload(Type requestedType, uint32_t* payloadSize=nullptr) const;
+private:
+ const TypedBytes* first() const;
+ const TypedBytes* next(const TypedBytes*) const;
+};
+
+
+//
+// Information about a mach-o file
+//
+struct VIS_HIDDEN Image : ContainerTypedBytes
+{
+ enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 };
+
+ size_t size() const;
+ ImageNum imageNum() const;
+ bool representsImageNum(ImageNum num) const; // imageNum() or isOverrideOfDyldCacheImage()
+ uint32_t maxLoadCount() const;
+ const char* path() const;
+ const char* leafName() const;
+ bool getUuid(uuid_t) const;
+ bool isInvalid() const;
+ bool inDyldCache() const;
+ bool hasObjC() const;
+ bool hasInitializers() const;
+ bool isBundle() const;
+ bool isDylib() const;
+ bool isExecutable() const;
+ bool hasWeakDefs() const;
+ bool mayHavePlusLoads() const;
+ bool is64() const;
+ bool neverUnload() const;
+ bool cwdMustBeThisDir() const;
+ bool isPlatformBinary() const;
+ bool overridableDylib() const;
+ bool hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const;
+ bool hasCdHash(uint8_t cdHash[20]) const;
+ void forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const;
+ void forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const;
+ ImageNum dependentImageNum(uint32_t depIndex) const;
+ bool containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions=nullptr) const;
+ void forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+ void forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const;
+ void forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+ bool hasPathWithHash(const char* path, uint32_t hash) const;
+ bool isOverrideOfDyldCacheImage(ImageNum& cacheImageNum) const;
+ uint64_t textSize() const;
+
+ union ResolvedSymbolTarget
+ {
+ enum Kinds { kindRebase, kindSharedCache, kindImage, kindAbsolute };
+
+ struct Rebase {
+ uint64_t kind : 2, // kindRebase
+ unused : 62; // all zeros
+ };
+ struct SharedCache {
+ uint64_t kind : 2, // kindSharedCache
+ offset : 62;
+ };
+ struct Image {
+ uint64_t kind : 2, // kindImage
+ imageNum : 22, // ImageNum
+ offset : 40;
+ };
+ struct Absolute {
+ uint64_t kind : 2, // kindAbsolute
+ value : 62; // sign extended
+ };
+ Rebase rebase;
+ SharedCache sharedCache;
+ Image image;
+ Absolute absolute;
+ uint64_t raw;
+
+ bool operator==(const ResolvedSymbolTarget& rhs) const {
+ return (raw == rhs.raw);
+ }
+ bool operator!=(const ResolvedSymbolTarget& rhs) const {
+ return (raw != rhs.raw);
+ }
+ };
+
+ typedef MachOLoaded::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk;
+
+ // the following are only valid if inDyldCache() returns true
+ uint32_t cacheOffset() const;
+ uint32_t patchStartIndex() const;
+ uint32_t patchCount() const;
+ void forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+
+
+ // the following are only valid if inDyldCache() returns false
+ uint64_t vmSizeToMap() const;
+ uint64_t sliceOffsetInFile() const;
+ bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+ bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) 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 forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
+ void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
+ void (^chainedFixupStart)(uint64_t imageOffsetStart, const Array<ResolvedSymbolTarget>& targets, bool& stop)) const;
+ void forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
+ void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const;
+ static void forEachChainedFixup(void* imageLoadAddress, uint64_t imageOffsetChainStart,
+ void (^chainedFixupStart)(uint64_t* fixupLoc, ChainedFixupPointerOnDisk fixupInfo, bool& stop));
+
+ static_assert(sizeof(ResolvedSymbolTarget) == 8, "Overflow in size of SymbolTargetLocation");
+
+ static uint32_t hashFunction(const char*);
+
+
+ // only in Image for cached dylibs
+ struct PatchableExport
+ {
+ struct PatchLocation
+ {
+ uint64_t cacheOffset : 32,
+ addend : 12, // +/- 2048
+ authenticated : 1,
+ usesAddressDiversity : 1,
+ key : 2,
+ discriminator : 16;
+
+ PatchLocation(size_t cacheOffset, uint64_t addend);
+ PatchLocation(size_t cacheOffset, uint64_t addend, dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo);
+
+ uint64_t getAddend() const {
+ uint64_t unsingedAddend = addend;
+ int64_t signedAddend = (int64_t)unsingedAddend;
+ signedAddend = (signedAddend << 52) >> 52;
+ return (uint64_t)signedAddend;
+ }
+
+ const char* keyName() const;
+ bool operator==(const PatchLocation& other) const {
+ return this->cacheOffset == other.cacheOffset;
+ }
+ };
+
+ uint32_t cacheOffsetOfImpl;
+ uint32_t patchLocationsCount;
+ PatchLocation patchLocations[];
+ // export name
+ };
+ uint32_t patchableExportCount() const;
+ void forEachPatchableExport(void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const;
+ void forEachPatchableUseOfExport(uint32_t cacheOffsetOfImpl, void (^handler)(PatchableExport::PatchLocation patchLocation)) const;
+
+private:
+ friend struct Closure;
+ friend class ImageWriter;
+ friend class ClosureBuilder;
+ friend class LaunchClosureWriter;
+
+ uint32_t pageSize() const;
+
+ struct Flags
+ {
+ uint64_t imageNum : 16,
+ maxLoadCount : 12,
+ isInvalid : 1, // an error occurred creating the info for this image
+ has16KBpages : 1,
+ is64 : 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,
+ isDylib : 1,
+ isExecutable : 1,
+ overridableDylib : 1, // only applicable to cached dylibs
+ inDyldCache : 1,
+ padding : 21;
+ };
+
+ const Flags& getFlags() const;
+
+ struct PathAndHash
+ {
+ uint32_t hash;
+ char path[];
+ };
+
+ // 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;
+ };
+
+ struct CodeSignatureLocation
+ {
+ uint32_t fileOffset;
+ uint32_t fileSize;
+ };
+
+ struct FileInfo
+ {
+ uint64_t inode;
+ uint64_t modTime;
+ };
+
+ struct FairPlayRange
+ {
+ uint32_t textPageCount : 28,
+ textStartPage : 4;
+ };
+
+ struct MappingInfo
+ {
+ uint32_t totalVmPages;
+ uint32_t sliceOffsetIn4K;
+ };
+
+ struct LinkedImage {
+
+ LinkedImage() : imgNum(0), linkKind(0) {
+ }
+ LinkedImage(LinkKind k, ImageNum num) : imgNum(num), linkKind((uint32_t)k) {
+ assert((num & 0xC0000000) == 0);
+ }
+
+ LinkKind kind() const { return (LinkKind)linkKind; }
+ ImageNum imageNum() const { return imgNum; }
+ void clearKind() { linkKind = 0; }
+
+ bool operator==(const LinkedImage& rhs) const {
+ return (linkKind == rhs.linkKind) && (imgNum == rhs.imgNum);
+ }
+ bool operator!=(const LinkedImage& rhs) const {
+ return (linkKind != rhs.linkKind) || (imgNum != rhs.imgNum);
+ }
+ private:
+ uint32_t imgNum : 30,
+ linkKind : 2; // LinkKind
+ };
+
+ const Array<LinkedImage> dependentsArray() const;
+
+ struct RebasePattern
+ {
+ uint32_t repeatCount : 20,
+ contigCount : 8, // how many contiguous pointers neeed rebasing
+ skipCount : 4; // how many pointers to skip between contig groups
+ // If contigCount == 0, then there are no rebases for this entry,
+ // instead it advances the rebase location by repeatCount*skipCount.
+ // If all fields are zero, then the rebase position is reset to the start.
+ // This is to support old binaries with some non-monotonically-increasing rebases.
+ };
+ const Array<RebasePattern> rebaseFixups() const;
+
+ struct BindPattern
+ {
+ Image::ResolvedSymbolTarget target;
+ uint64_t startVmOffset : 40, // max 1TB offset
+ skipCount : 8,
+ repeatCount : 16;
+ };
+ const Array<BindPattern> bindFixups() const;
+
+ struct TextFixupPattern
+ {
+ Image::ResolvedSymbolTarget target;
+ uint32_t startVmOffset;
+ uint16_t repeatCount;
+ uint16_t skipCount;
+ };
+ const Array<TextFixupPattern> textFixups() const;
+
+ // for use with chained fixups
+ const Array<uint64_t> chainedStarts() const;
+ const Array<Image::ResolvedSymbolTarget> chainedTargets() const;
+
+};
+
+/*
+ Dyld cache patching notes:
+
+ The dyld cache needs to be patched to support interposing and dylib "roots".
+
+ For cached dylibs overrides:
+ Closure build time:
+ 1) LoadedImages will contain the new dylib, so all symbol look ups
+ will naturally find new impl. Only dyld cache needs special help.
+ 2) LoadedImages entry will have flag for images that override cache.
+ 3) When setting Closure attributes, if flag is set, builder will
+ iterate PatchableExport entries in Image* from cache and create
+ a PatchEntry for each.
+ Runtime:
+ 1) [lib]dyld will iterate PatchEntry in closure and patch cache
+
+ For interposing:
+ Closure build time:
+ 1) After Images built, if __interpose section(s) exist, builder will
+ build InterposingTuple entries for closure
+ 2) For being-built closure and launch closure, apply any InterposingTuple
+ to modify Image fixups before Image finalized.
+ 3) Builder will find PatchableExport entry that matchs stock Impl
+ and add PatchEntry to closure for it.
+ Runtime:
+ 1) When closure is loaded (launch or dlopen) PatchEntries are
+ applied (exactly once) to whole cache.
+ 2) For each DlopenClosure loaded, any InterposeTuples in *launch* closure
+ are applied to all new images in new DlopenClosure.
+
+ For weak-def coalesing:
+ Closure build time:
+ 1) weak_bind entries are turned into -3 ordinal lookup which search through images
+ in load order for first def (like flat). This fixups up all images not in cache.
+ 2) When processing -3 ordinals, it continues past first found and if any images
+ past it are in dyld cache and export that same symbol, a PatchEntry is added to
+ closure to fix up all cached uses of that symbol.
+ 3) If a weak_bind has strong bit set (no fixup, just def), all images from the dyld
+ cache are checked to see if the export that symbol, if so, a PatchEntry is added
+ to the closure.
+ Runtime:
+ 1) When closure is loaded (launch or dlopen) PatchEntries are
+ applied (exactly once) to whole cache.
+
+*/
+
+
+//
+// An array (accessible by index) list of Images
+//
+struct VIS_HIDDEN ImageArray : public TypedBytes
+{
+ size_t size() const;
+ size_t startImageNum() const;
+ uint32_t imageCount() const;
+ void forEachImage(void (^callback)(const Image* image, bool& stop)) const;
+ bool hasPath(const char* path, ImageNum& num) const;
+ const Image* imageForNum(ImageNum) const;
+
+ static const Image* findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum);
+
+private:
+ friend class ImageArrayWriter;
+
+ uint32_t firstImageNum;
+ uint32_t count;
+ uint32_t offsets[];
+ // Image data
+};
+
+
+struct InterposingTuple
+{
+ Image::ResolvedSymbolTarget stockImplementation;
+ Image::ResolvedSymbolTarget newImplementation;
+};
+
+
+//
+// Describes how dyld should load a set of mach-o files
+//
+struct VIS_HIDDEN Closure : public ContainerTypedBytes
+{
+ size_t size() const;
+ const ImageArray* images() const;
+ ImageNum topImage() const;
+ void deallocate() const;
+
+ friend class ClosureWriter;
+
+ struct PatchEntry
+ {
+ ImageNum overriddenDylibInCache;
+ uint32_t exportCacheOffset;
+ Image::ResolvedSymbolTarget replacement;
+ };
+ void forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const;
+};
+
+
+//
+// Describes how dyld should launch a main executable
+//
+struct VIS_HIDDEN LaunchClosure : public Closure
+{
+ bool builtAgainstDyldCache(uuid_t cacheUUID) const;
+ const char* bootUUID() const;
+ void forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const;
+ void forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
+ ImageNum libSystemImageNum() const;
+ void libDyldEntry(Image::ResolvedSymbolTarget& loc) const;
+ bool mainEntry(Image::ResolvedSymbolTarget& mainLoc) const;
+ bool startEntry(Image::ResolvedSymbolTarget& startLoc) const;
+ uint32_t initialLoadCount() const;
+ void forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const;
+ bool usedAtPaths() const;
+ bool usedFallbackPaths() const;
+
+
+private:
+ friend class LaunchClosureWriter;
+
+ struct Flags
+ {
+ uint32_t usedAtPaths : 1,
+ usedFallbackPaths : 1,
+ initImageCount : 16,
+ padding : 14;
+ };
+ const Flags& getFlags() const;
+};
+
+
+
+//
+// Describes how dyld should dlopen() a mach-o file
+//
+struct VIS_HIDDEN DlopenClosure : public Closure
+{
+
+};
+
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+#endif // Closures_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
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+
+#include "mach-o/dyld_priv.h"
+
+#include "ClosureWriter.h"
+#include "ClosureBuilder.h"
+#include "MachOAnalyzer.h"
+#include "libdyldEntryVector.h"
+#include "Tracing.h"
+
+namespace dyld3 {
+namespace closure {
+
+const DlopenClosure* ClosureBuilder::sRetryDlopenClosure = (const DlopenClosure*)(-1);
+
+ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+ const PathOverrides& pathOverrides, AtPath atPathHandling, LaunchErrorInfo* errorInfo,
+ const char* archName, Platform platform,
+ const CacheDylibsBindingHandlers* handlers)
+ : _fileSystem(fileSystem), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archName(archName), _platform(platform), _startImageNum(startImageNum),
+ _handlers(handlers), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive)
+{
+ if ( dyldCache != nullptr ) {
+ _dyldImageArray = dyldCache->cachedDylibsImageArray();
+ if ( (dyldCache->header.otherImageArrayAddr != 0) && (dyldCache->header.progClosuresSize == 0) )
+ _makingClosuresInCache = true;
+ }
+}
+
+
+ClosureBuilder::~ClosureBuilder() {
+ if ( _tempPaths != nullptr )
+ PathPool::deallocate(_tempPaths);
+ if ( _mustBeMissingPaths != nullptr )
+ PathPool::deallocate(_mustBeMissingPaths);
+}
+
+bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, bool staticLinkage, bool allowOther)
+{
+ __block bool result = false;
+
+ _pathOverrides.forEachPathVariant(loadPath, ^(const char* possiblePath, bool isFallbackPath, bool& stop) {
+ bool unmapWhenDone = false;
+ bool contentRebased = false;
+ bool hasInits = false;
+ bool fileFound = false;
+ bool markNeverUnload = staticLinkage ? forImageChain.image.markNeverUnload : false;
+ ImageNum overrideImageNum = 0;
+ ImageNum foundImageNum = 0;
+ const MachOAnalyzer* mh = nullptr;
+ const char* filePath = nullptr;
+ LoadedFileInfo loadedFileInfo;
+
+ // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH
+ bool isRPath = (strncmp(possiblePath, "@rpath/", 7) == 0);
+
+ // passing a leaf name to dlopen() allows rpath searching for it
+ bool implictRPath = !staticLinkage && (loadPath[0] != '/') && (loadPath == possiblePath) && (_atPathHandling != AtPath::none);
+
+ // expand @ paths
+ const char* prePathVarExpansion = possiblePath;
+ possiblePath = resolvePathVar(possiblePath, forImageChain, implictRPath);
+ if ( prePathVarExpansion != possiblePath )
+ _atPathUsed = true;
+
+ // look at already loaded images
+ const char* leafName = strrchr(possiblePath, '/');
+ for (BuilderLoadedImage& li: _loadedImages) {
+ if ( strcmp(li.path(), possiblePath) == 0 ) {
+ foundImage = &li;
+ result = true;
+ stop = true;
+ return;
+ }
+ else if ( isRPath ) {
+ // Special case @rpath/ because name in li.fileInfo.path is full path.
+ // Getting installName is expensive, so first see if an already loaded image
+ // has same leaf name and if so see if its installName matches request @rpath
+ if (const char* aLeaf = strrchr(li.path(), '/')) {
+ if ( strcmp(aLeaf, leafName) == 0 ) {
+ if ( li.loadAddress()->isDylib() && (strcmp(loadPath, li.loadAddress()->installName()) == 0) ) {
+ foundImage = &li;
+ result = true;
+ stop = true;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // look to see if image already loaded via a different symlink
+ if ( _fileSystem.fileExists(possiblePath, &loadedFileInfo.inode, &loadedFileInfo.mtime) ) {
+ fileFound = true;
+ for (BuilderLoadedImage& li: _loadedImages) {
+ if ( (li.loadedFileInfo.inode == loadedFileInfo.inode) && (li.loadedFileInfo.mtime == loadedFileInfo.mtime) ) {
+ foundImage = &li;
+ result = true;
+ stop = true;
+ return;
+ }
+ }
+ }
+
+ // look in dyld cache
+ filePath = possiblePath;
+ char realPath[MAXPATHLEN];
+ if ( _dyldImageArray != nullptr && (_dyldCache->header.formatVersion == dyld3::closure::kFormatVersion) ) {
+ uint32_t dyldCacheImageIndex;
+ bool foundInCache = _dyldCache->hasImagePath(possiblePath, dyldCacheImageIndex);
+ if ( !foundInCache && fileFound ) {
+ // see if this is an OS dylib/bundle with a pre-built dlopen closure
+ if ( allowOther ) {
+ if (const dyld3::closure::Image* otherImage = _dyldCache->findDlopenOtherImage(possiblePath) ) {
+ uint64_t expectedInode;
+ uint64_t expectedModTime;
+ if ( !otherImage->isInvalid() ) {
+ bool hasInodeInfo = otherImage->hasFileModTimeAndInode(expectedInode, expectedModTime);
+ // use pre-built Image if it does not have mtime/inode or it does and it has matches current file info
+ if ( !hasInodeInfo || ((expectedInode == loadedFileInfo.inode) && (expectedModTime == loadedFileInfo.mtime)) ) {
+ loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, possiblePath, _archName, _platform);
+ if ( _diag.noError() ) {
+ mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+ foundImageNum = otherImage->imageNum();
+ unmapWhenDone = true;
+ contentRebased = false;
+ hasInits = otherImage->hasInitializers() || otherImage->mayHavePlusLoads();
+ }
+ }
+ }
+ }
+ }
+ // if not found in cache, may be a symlink to something in cache
+ if ( mh == nullptr ) {
+ if ( _fileSystem.getRealPath(possiblePath, realPath) ) {
+ foundInCache = _dyldCache->hasImagePath(realPath, dyldCacheImageIndex);
+ if ( foundInCache ) {
+ filePath = realPath;
+ #if BUILDING_LIBDYLD
+ // handle case where OS dylib was updated after this process launched
+ if ( foundInCache ) {
+ for (BuilderLoadedImage& li: _loadedImages) {
+ if ( strcmp(li.path(), realPath) == 0 ) {
+ foundImage = &li;
+ result = true;
+ stop = true;
+ return;
+ }
+ }
+ }
+ #endif
+ }
+ }
+ }
+ }
+
+ // if using a cached dylib, look to see if there is an override
+ if ( foundInCache ) {
+ ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1;
+ bool useCache = true;
+ markNeverUnload = true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime
+ const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum);
+ if ( image->overridableDylib() ) {
+ if ( fileFound && (_platform == MachOFile::currentPlatform()) ) {
+ uint64_t expectedInode;
+ uint64_t expectedModTime;
+ if ( image->hasFileModTimeAndInode(expectedInode, expectedModTime) ) {
+ // macOS where dylibs remain on disk. only use cache if mtime and inode have not changed
+ useCache = ( (loadedFileInfo.inode == expectedInode) && (loadedFileInfo.mtime == expectedModTime) );
+ }
+ else if ( _makingClosuresInCache ) {
+ // during iOS cache build, don't look at files on disk, use ones in cache
+ useCache = true;
+ }
+ else {
+ // iOS internal build. Any disk on cache overrides cache
+ useCache = false;
+ }
+ }
+ if ( !useCache )
+ overrideImageNum = dyldCacheImageNum;
+ }
+ if ( useCache ) {
+ foundImageNum = dyldCacheImageNum;
+ mh = (MachOAnalyzer*)_dyldCache->getIndexedImageEntry(foundImageNum-1, loadedFileInfo.mtime, loadedFileInfo.inode);
+ unmapWhenDone = false;
+ // if we are building ImageArray in dyld cache, content is not rebased
+ contentRebased = !_makingDyldCacheImages && _dyldCacheIsLive;
+ hasInits = image->hasInitializers() || image->mayHavePlusLoads();
+ }
+ }
+ }
+
+ // If we are building the cache, and don't find an image, then it might be weak so just return
+ if (_makingDyldCacheImages) {
+ addMustBeMissingPath(possiblePath);
+ return;
+ }
+
+ // if not found yet, mmap file
+ if ( mh == nullptr ) {
+ loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, filePath, _archName, _platform);
+ mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+ if ( mh == nullptr ) {
+ // Don't add must be missing paths for dlopen as we don't cache dlopen closures
+ if (_isLaunchClosure) {
+ addMustBeMissingPath(possiblePath);
+ }
+ return;
+ }
+ if ( staticLinkage ) {
+ // LC_LOAD_DYLIB can only link with dylibs
+ if ( !mh->isDylib() ) {
+ _diag.error("not a dylib");
+ return;
+ }
+ }
+ else if ( mh->isMainExecutable() ) {
+ // when dlopen()ing a main executable, it must be dynamic Position Independent Executable
+ if ( !mh->isPIE() || !mh->isDynamicExecutable() ) {
+ _diag.error("not PIE");
+ return;
+ }
+ }
+ foundImageNum = _startImageNum + _nextIndex++;
+ unmapWhenDone = true;
+ } else {
+ loadedFileInfo.fileContent = mh;
+ }
+
+ // if path is not original path
+ if ( filePath != loadPath ) {
+ // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent
+ filePath = strdup_temp(filePath);
+ // check if this overrides what would have been found in cache
+ if ( overrideImageNum == 0 ) {
+ if ( _dyldImageArray != nullptr ) {
+ uint32_t dyldCacheImageIndex;
+ if ( _dyldCache->hasImagePath(loadPath, dyldCacheImageIndex) ) {
+ ImageNum possibleOverrideNum = dyldCacheImageIndex+1;
+ if ( possibleOverrideNum != foundImageNum )
+ overrideImageNum = possibleOverrideNum;
+ }
+ }
+ }
+ }
+
+ if ( !markNeverUnload ) {
+ // If the parent didn't force us to be never unload, other conditions still may
+ if ( mh->hasThreadLocalVariables() ) {
+ markNeverUnload = true;
+ } else if ( mh->hasObjC() && mh->isDylib() ) {
+ markNeverUnload = true;
+ } else {
+ // record if image has DOF sections
+ __block bool hasDOFs = false;
+ mh->forEachDOFSection(_diag, ^(uint32_t offset) {
+ hasDOFs = true;
+ });
+ if ( hasDOFs )
+ markNeverUnload = true;
+ }
+ }
+
+ // Set the path again just in case it was strdup'ed.
+ loadedFileInfo.path = filePath;
+
+ // add new entry
+ BuilderLoadedImage entry;
+ entry.loadedFileInfo = loadedFileInfo;
+ entry.imageNum = foundImageNum;
+ entry.unmapWhenDone = unmapWhenDone;
+ entry.contentRebased = contentRebased;
+ entry.hasInits = hasInits;
+ entry.markNeverUnload = markNeverUnload;
+ entry.rtldLocal = false;
+ entry.isBadImage = false;
+ entry.overrideImageNum = overrideImageNum;
+ _loadedImages.push_back(entry);
+ foundImage = &_loadedImages.back();
+ if ( isFallbackPath )
+ _fallbackPathUsed = true;
+ stop = true;
+ result = true;
+ }, _platform);
+
+ return result;
+}
+
+bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[])
+{
+ switch ( _atPathHandling ) {
+ case AtPath::none:
+ return false;
+ case AtPath::onlyInRPaths:
+ if ( !fromLCRPATH ) {
+ // <rdar://42360708> allow @loader_path in LC_LOAD_DYLIB during dlopen()
+ if ( _isLaunchClosure )
+ return false;
+ }
+ break;
+ case AtPath::all:
+ break;
+ }
+ if ( strncmp(loadPath, "@loader_path/", 13) != 0 )
+ return false;
+
+ strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ strcpy(lastSlash+1, &loadPath[13]);
+ return true;
+ }
+ return false;
+}
+
+bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[])
+{
+ switch ( _atPathHandling ) {
+ case AtPath::none:
+ return false;
+ case AtPath::onlyInRPaths:
+ if ( !fromLCRPATH )
+ return false;
+ break;
+ case AtPath::all:
+ break;
+ }
+ if ( strncmp(loadPath, "@executable_path/", 17) != 0 )
+ return false;
+
+ if ( _atPathHandling != AtPath::all )
+ return false;
+
+ strlcpy(fixedPath, _loadedImages[_mainProgLoadIndex].path(), PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ strcpy(lastSlash+1, &loadPath[17]);
+ return true;
+ }
+ return false;
+}
+
+const char* ClosureBuilder::resolvePathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath)
+{
+ // don't expand @ path if disallowed
+ if ( (_atPathHandling == AtPath::none) && (loadPath[0] == '@') )
+ return loadPath;
+
+ // quick out if not @ path or not implicit rpath
+ if ( !implictRPath && (loadPath[0] != '@') )
+ return loadPath;
+
+ // expand @loader_path
+ BLOCK_ACCCESSIBLE_ARRAY(char, tempPath, PATH_MAX); // read as: char tempPath[PATH_MAX];
+ if ( expandAtLoaderPath(loadPath, false, forImageChain.image, tempPath) )
+ return strdup_temp(tempPath);
+
+ // expand @executable_path
+ if ( expandAtExecutablePath(loadPath, false, tempPath) )
+ return strdup_temp(tempPath);
+
+ // expand @rpath
+ const char* rpathTail = nullptr;
+ char implicitRpathBuffer[PATH_MAX];
+ if ( strncmp(loadPath, "@rpath/", 7) == 0 ) {
+ // note: rpathTail starts with '/'
+ rpathTail = &loadPath[6];
+ }
+ else if ( implictRPath ) {
+ // make rpathTail starts with '/'
+ strlcpy(implicitRpathBuffer, "/", PATH_MAX);
+ strlcat(implicitRpathBuffer, loadPath, PATH_MAX);
+ rpathTail = implicitRpathBuffer;
+ }
+ if ( rpathTail != nullptr ) {
+ // rpath is expansion is technically a stack of rpath dirs built starting with main executable and pushing
+ // LC_RPATHS from each dylib as they are recursively loaded. Our imageChain represents that stack.
+ __block const char* result = nullptr;
+ for (const LoadedImageChain* link = &forImageChain; (link != nullptr) && (result == nullptr); link = link->previous) {
+ link->image.loadAddress()->forEachRPath(^(const char* rPath, bool& stop) {
+ // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.fileInfo.path);
+ if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, tempPath) ) {
+ strlcat(tempPath, rpathTail, PATH_MAX);
+ }
+ else {
+ strlcpy(tempPath, rPath, PATH_MAX);
+ strlcat(tempPath, rpathTail, PATH_MAX);
+ }
+ if ( _fileSystem.fileExists(tempPath) ) {
+ stop = true;
+ result = strdup_temp(tempPath);
+ }
+ else {
+ // Don't add must be missing paths for dlopen as we don't cache dlopen closures
+ if (_isLaunchClosure) {
+ addMustBeMissingPath(tempPath);
+ }
+ }
+ });
+ }
+ if ( result != nullptr )
+ return result;
+ }
+
+ return loadPath;
+}
+
+const char* ClosureBuilder::strdup_temp(const char* path)
+{
+ if ( _tempPaths == nullptr )
+ _tempPaths = PathPool::allocate();
+ return _tempPaths->add(path);
+}
+
+void ClosureBuilder::addMustBeMissingPath(const char* path)
+{
+ //fprintf(stderr, "must be missing: %s\n", path);
+ if ( _mustBeMissingPaths == nullptr )
+ _mustBeMissingPaths = PathPool::allocate();
+ _mustBeMissingPaths->add(path);
+}
+
+ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum imageNum)
+{
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.imageNum == imageNum ) {
+ return li;
+ }
+ }
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.overrideImageNum == imageNum ) {
+ return li;
+ }
+ }
+ assert(0 && "LoadedImage not found");
+}
+
+ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(const MachOAnalyzer* mh)
+{
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.loadAddress() == mh ) {
+ return li;
+ }
+ }
+ assert(0 && "LoadedImage not found");
+}
+
+const MachOAnalyzer* ClosureBuilder::machOForImageNum(ImageNum imageNum)
+{
+ return findLoadedImage(imageNum).loadAddress();
+}
+
+const MachOAnalyzer* ClosureBuilder::findDependent(const MachOLoaded* mh, uint32_t depIndex)
+{
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( li.loadAddress() == mh ) {
+ if (li.isBadImage) {
+ // Bad image duting building group 1 closures, so the dependents array
+ // is potentially incomplete.
+ return nullptr;
+ }
+ ImageNum childNum = li.dependents[depIndex].imageNum();
+ return machOForImageNum(childNum);
+ }
+ }
+ return nullptr;
+}
+
+ImageNum ClosureBuilder::imageNumForMachO(const MachOAnalyzer* mh)
+{
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( li.loadAddress() == mh ) {
+ return li.imageNum;
+ }
+ }
+ assert(0 && "unknown mach-o");
+ return 0;
+}
+
+void ClosureBuilder::recursiveLoadDependents(LoadedImageChain& forImageChain)
+{
+ // if dependents is set, then we have already loaded this
+ if ( forImageChain.image.dependents.begin() != nullptr )
+ return;
+
+ uintptr_t startDepIndex = _dependencies.count();
+ // add dependents
+ __block uint32_t depIndex = 0;
+ forImageChain.image.loadAddress()->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ Image::LinkKind kind = Image::LinkKind::regular;
+ if ( isWeak )
+ kind = Image::LinkKind::weak;
+ else if ( isReExport )
+ kind = Image::LinkKind::reExport;
+ else if ( isUpward )
+ kind = Image::LinkKind::upward;
+ BuilderLoadedImage* foundImage;
+ if ( findImage(loadPath, forImageChain, foundImage, true, false) ) {
+ // verify this is compatable dylib version
+ if ( foundImage->loadAddress()->filetype != MH_DYLIB ) {
+ _diag.error("found '%s' which is not a dylib. Needed by '%s'", foundImage->path(), forImageChain.image.path());
+ }
+ else {
+ const char* installName;
+ uint32_t foundCompatVers;
+ uint32_t foundCurrentVers;
+ foundImage->loadAddress()->getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers);
+ if ( (foundCompatVers < compatVersion) && foundImage->loadAddress()->enforceCompatVersion() ) {
+ char foundStr[32];
+ char requiredStr[32];
+ MachOFile::packedVersionToString(foundCompatVers, foundStr);
+ MachOFile::packedVersionToString(compatVersion, requiredStr);
+ _diag.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'",
+ foundImage->path(), foundStr, requiredStr, forImageChain.image.path());
+ }
+ }
+ if ( _diag.noError() )
+ _dependencies.push_back(Image::LinkedImage(kind, foundImage->imageNum));
+ }
+ else if ( isWeak ) {
+ _dependencies.push_back(Image::LinkedImage(Image::LinkKind::weak, kMissingWeakLinkedImage));
+ }
+ else {
+ BLOCK_ACCCESSIBLE_ARRAY(char, extra, 4096);
+ extra[0] = '\0';
+ const char* targetLeaf = strrchr(loadPath, '/');
+ if ( targetLeaf == nullptr )
+ targetLeaf = loadPath;
+ if ( _mustBeMissingPaths != nullptr ) {
+ strcpy(extra, ", tried: ");
+ _mustBeMissingPaths->forEachPath(^(const char* aPath) {
+ const char* aLeaf = strrchr(aPath, '/');
+ if ( aLeaf == nullptr )
+ aLeaf = aPath;
+ if ( strcmp(targetLeaf, aLeaf) == 0 ) {
+ strlcat(extra, "'", 4096);
+ strlcat(extra, aPath, 4096);
+ strlcat(extra, "' ", 4096);
+ }
+ });
+ }
+ if ( _diag.hasError() ) {
+ #if BUILDING_CACHE_BUILDER
+ std::string errorMessageBuffer = _diag.errorMessage();
+ const char* msg = errorMessageBuffer.c_str();
+ #else
+ const char* msg = _diag.errorMessage();
+ #endif
+ char msgCopy[strlen(msg)+4];
+ strcpy(msgCopy, msg);
+ _diag.error("dependent dylib '%s' not found for '%s'. %s", loadPath, forImageChain.image.path(), msgCopy);
+ }
+ else {
+ _diag.error("dependent dylib '%s' not found for '%s'%s", loadPath, forImageChain.image.path(), extra);
+ }
+ if ( _launchErrorInfo != nullptr ) {
+ _launchErrorInfo->kind = DYLD_EXIT_REASON_DYLIB_MISSING;
+ _launchErrorInfo->clientOfDylibPath = forImageChain.image.path();
+ _launchErrorInfo->targetDylibPath = loadPath;
+ _launchErrorInfo->symbol = nullptr;
+ }
+ }
+ ++depIndex;
+ if ( _diag.hasError() )
+ stop = true;
+ });
+ if ( _diag.hasError() )
+ return;
+ forImageChain.image.dependents = _dependencies.subArray(startDepIndex, depIndex);
+
+ // breadth first recurse
+ for (Image::LinkedImage dep : forImageChain.image.dependents) {
+ // don't recurse upwards
+ if ( dep.kind() == Image::LinkKind::upward )
+ continue;
+ // don't recurse down missing weak links
+ if ( (dep.kind() == Image::LinkKind::weak) && (dep.imageNum() == kMissingWeakLinkedImage) )
+ continue;
+ BuilderLoadedImage& depLoadedImage = findLoadedImage(dep.imageNum());
+ LoadedImageChain chain = { &forImageChain, depLoadedImage };
+ recursiveLoadDependents(chain);
+ if ( _diag.hasError() )
+ break;
+ }
+}
+
+void ClosureBuilder::loadDanglingUpwardLinks()
+{
+ bool danglingFixed;
+ do {
+ danglingFixed = false;
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.dependents.begin() == nullptr ) {
+ // this image has not have dependents set (probably a dangling upward link or referenced by upward link)
+ LoadedImageChain chain = { nullptr, li };
+ recursiveLoadDependents(chain);
+ danglingFixed = true;
+ break;
+ }
+ }
+ } while (danglingFixed && _diag.noError());
+}
+
+bool ClosureBuilder::overridableDylib(const BuilderLoadedImage& forImage)
+{
+ // only set on dylibs in the dyld shared cache
+ if ( !_makingDyldCacheImages )
+ return false;
+
+ // on macOS dylibs always override cache
+ if ( _platform == Platform::macOS )
+ return true;
+
+ // on embedded platforms with Internal cache, allow overrides
+ if ( !_makingCustomerCache )
+ return true;
+
+ // embedded platform customer caches, no overrides
+ return false; // FIXME, allow libdispatch.dylib to be overridden
+}
+
+void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+ const MachOAnalyzer* macho = forImage.loadAddress();
+ // set ImageNum
+ writer.setImageNum(forImage.imageNum);
+
+ // set flags
+ writer.setHasWeakDefs(macho->hasWeakDefs());
+ writer.setIsBundle(macho->isBundle());
+ writer.setIsDylib(macho->isDylib());
+ writer.setIs64(macho->is64());
+ writer.setIsExecutable(macho->isMainExecutable());
+ writer.setUses16KPages(macho->uses16KPages());
+ writer.setOverridableDylib(overridableDylib(forImage));
+ writer.setInDyldCache(macho->inDyldCache());
+ if ( macho->hasObjC() ) {
+ writer.setHasObjC(true);
+ bool hasPlusLoads = macho->hasPlusLoadMethod(_diag);
+ writer.setHasPlusLoads(hasPlusLoads);
+ if ( hasPlusLoads )
+ forImage.hasInits = true;
+ }
+ else {
+ writer.setHasObjC(false);
+ writer.setHasPlusLoads(false);
+ }
+
+ if ( forImage.markNeverUnload ) {
+ writer.setNeverUnload(true);
+ }
+
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+ // shared cache not built by dyld or libdyld.dylib, so must be real file
+ writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
+#else
+ if ( _platform == Platform::macOS ) {
+ if ( macho->inDyldCache() && !_dyldCache->header.dylibsExpectedOnDisk ) {
+ // don't add file info for shared cache files mastered out of final file system
+ }
+ else {
+ // file is either not in cache or is in cache but not mastered out
+ writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
+ }
+ }
+ else {
+ // all other platforms, cache is built off-device, so inodes are not known
+ }
+#endif
+
+ // add info on how to load image
+ if ( !macho->inDyldCache() ) {
+ writer.setMappingInfo(forImage.loadedFileInfo.sliceOffset, macho->mappedSize());
+ // add code signature, if signed
+ uint32_t codeSigFileOffset;
+ uint32_t codeSigSize;
+ if ( macho->hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
+ writer.setCodeSignatureLocation(codeSigFileOffset, codeSigSize);
+ uint8_t cdHash[20];
+ if ( macho->getCDHash(cdHash) )
+ writer.setCDHash(cdHash);
+ }
+ // add FairPlay encryption range if encrypted
+ uint32_t fairPlayFileOffset;
+ uint32_t fairPlaySize;
+ if ( macho->isFairPlayEncrypted(fairPlayFileOffset, fairPlaySize) ) {
+ writer.setFairPlayEncryptionRange(fairPlayFileOffset, fairPlaySize);
+ }
+ }
+
+ // set path
+ writer.addPath(forImage.path());
+ if ( _aliases != nullptr ) {
+ for (const CachedDylibAlias& alias : *_aliases) {
+ if ( strcmp(alias.realPath, forImage.path()) == 0 )
+ writer.addPath(alias.aliasPath);
+ }
+ }
+
+ // set uuid, if has one
+ uuid_t uuid;
+ if ( macho->getUuid(uuid) )
+ writer.setUUID(uuid);
+
+ // set dependents
+ writer.setDependents(forImage.dependents);
+
+ // set segments
+ addSegments(writer, macho);
+
+ // record if this dylib overrides something in the cache
+ if ( forImage.overrideImageNum != 0 ) {
+ writer.setAsOverrideOf(forImage.overrideImageNum);
+ const char* overridePath = _dyldImageArray->imageForNum(forImage.overrideImageNum)->path();
+ writer.addPath(overridePath);
+ if ( strcmp(overridePath, "/usr/lib/system/libdyld.dylib") == 0 )
+ _libDyldImageNum = forImage.imageNum;
+ else if ( strcmp(overridePath, "/usr/lib/libSystem.B.dylib") == 0 )
+ _libSystemImageNum = forImage.imageNum;
+ }
+
+
+ // do fix up info for non-cached, and cached if building cache
+ if ( !macho->inDyldCache() || _makingDyldCacheImages ) {
+ if ( macho->hasChainedFixups() ) {
+ addChainedFixupInfo(writer, forImage);
+ }
+ else {
+ if ( _handlers != nullptr ) {
+ reportRebasesAndBinds(writer, forImage);
+ }
+ else {
+ addRebaseInfo(writer, macho);
+ if ( _diag.noError() )
+ addBindInfo(writer, forImage);
+ }
+ }
+ }
+ if ( _diag.hasError() ) {
+ writer.setInvalid();
+ return;
+ }
+
+ // add initializers
+ bool contentRebased = forImage.contentRebased;
+ __block unsigned initCount = 0;
+ macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
+ ++initCount;
+ }, _dyldCache);
+ if ( initCount != 0 ) {
+ BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets, initCount);
+ __block unsigned index = 0;
+ macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
+ initOffsets[index++] = offset;
+ }, _dyldCache);
+ writer.setInitOffsets(initOffsets, initCount);
+ forImage.hasInits = true;
+ }
+
+ // record if image has DOF sections
+ STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets, 256);
+ macho->forEachDOFSection(_diag, ^(uint32_t offset) {
+ dofSectionOffsets.push_back(offset);
+ });
+ if ( !dofSectionOffsets.empty() ) {
+ writer.setDofOffsets(dofSectionOffsets);
+ }
+
+}
+
+void ClosureBuilder::addSegments(ImageWriter& writer, const MachOAnalyzer* mh)
+{
+ const uint32_t segCount = mh->segmentCount();
+ if ( mh->inDyldCache() ) {
+ uint64_t cacheUnslideBaseAddress = _dyldCache->unslidLoadAddress();
+ BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment, segs, segCount);
+ mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
+ segs[info.segIndex] = { (uint32_t)(info.vmAddr-cacheUnslideBaseAddress), (uint32_t)info.vmSize, info.protections };
+ });
+ writer.setCachedSegments(segs, segCount);
+ }
+ else {
+ const uint32_t pageSize = (mh->uses16KPages() ? 0x4000 : 0x1000);
+ __block uint32_t diskSegIndex = 0;
+ __block uint32_t totalPageCount = 0;
+ __block uint32_t lastFileOffsetEnd = 0;
+ __block uint64_t lastVmAddrEnd = 0;
+ BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment, dsegs, segCount*3); // room for padding
+ mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
+ if ( (info.fileOffset != 0) && (info.fileOffset != lastFileOffsetEnd) ) {
+ Image::DiskSegment filePadding;
+ filePadding.filePageCount = (info.fileOffset - lastFileOffsetEnd)/pageSize;
+ filePadding.vmPageCount = 0;
+ filePadding.permissions = 0;
+ filePadding.paddingNotSeg = 1;
+ dsegs[diskSegIndex++] = filePadding;
+ }
+ if ( (lastVmAddrEnd != 0) && (info.vmAddr != lastVmAddrEnd) ) {
+ Image::DiskSegment vmPadding;
+ vmPadding.filePageCount = 0;
+ vmPadding.vmPageCount = (info.vmAddr - lastVmAddrEnd)/pageSize;
+ vmPadding.permissions = 0;
+ vmPadding.paddingNotSeg = 1;
+ dsegs[diskSegIndex++] = vmPadding;
+ totalPageCount += vmPadding.vmPageCount;
+ }
+ {
+ Image::DiskSegment segInfo;
+ segInfo.filePageCount = (info.fileSize+pageSize-1)/pageSize;
+ segInfo.vmPageCount = (info.vmSize+pageSize-1)/pageSize;
+ segInfo.permissions = info.protections & 7;
+ segInfo.paddingNotSeg = 0;
+ dsegs[diskSegIndex++] = segInfo;
+ totalPageCount += segInfo.vmPageCount;
+ if ( info.fileSize != 0 )
+ lastFileOffsetEnd = (uint32_t)(info.fileOffset + info.fileSize);
+ if ( info.vmSize != 0 )
+ lastVmAddrEnd = info.vmAddr + info.vmSize;
+ }
+ });
+ writer.setDiskSegments(dsegs, diskSegIndex);
+ }
+}
+
+void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh)
+{
+ const unsigned pointerSize = mh->pointerSize();
+ mh->forEachInterposingSection(_diag, ^(uint64_t sectVmOffset, uint64_t sectVmSize, bool &stop) {
+ const uint32_t entrySize = 2*pointerSize;
+ const uint32_t tupleCount = (uint32_t)(sectVmSize/entrySize);
+ BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple, resolvedTuples, tupleCount);
+ for (uint32_t i=0; i < tupleCount; ++i) {
+ resolvedTuples[i].stockImplementation.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ resolvedTuples[i].stockImplementation.absolute.value = 0;
+ resolvedTuples[i].newImplementation.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ resolvedTuples[i].newImplementation.absolute.value = 0;
+ }
+ image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &rebaseStop) {
+ if ( imageOffsetToRebase < sectVmOffset )
+ return;
+ if ( imageOffsetToRebase > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToRebase - sectVmOffset;
+ uint64_t rebaseIndex = offsetIntoSection/entrySize;
+ if ( rebaseIndex*entrySize != offsetIntoSection )
+ return;
+ const void* content = (uint8_t*)mh + imageOffsetToRebase;
+ uint64_t unslidTargetAddress = mh->is64() ? *(uint64_t*)content : *(uint32_t*)content;
+ resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+ resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
+ }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget bindTarget, bool &bindStop) {
+ if ( imageOffsetToBind < sectVmOffset )
+ return;
+ if ( imageOffsetToBind > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToBind - sectVmOffset;
+ uint64_t bindIndex = offsetIntoSection/entrySize;
+ if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+ return;
+ resolvedTuples[bindIndex].stockImplementation = bindTarget;
+ }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& chainStop) {
+ // walk each fixup in the chain
+ image->forEachChainedFixup((void*)mh, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
+ uint64_t imageOffsetToFixup = (uint64_t)fixupLoc - (uint64_t)mh;
+ if ( fixupInfo.authRebase.auth ) {
+#if SUPPORT_ARCH_arm64e
+ if ( fixupInfo.authBind.bind ) {
+ closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
+ if ( imageOffsetToFixup < sectVmOffset )
+ return;
+ if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+ uint64_t bindIndex = offsetIntoSection/entrySize;
+ if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+ return;
+ resolvedTuples[bindIndex].stockImplementation = bindTarget;
+ }
+ else {
+ if ( imageOffsetToFixup < sectVmOffset )
+ return;
+ if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+ uint64_t rebaseIndex = offsetIntoSection/entrySize;
+ if ( rebaseIndex*entrySize != offsetIntoSection )
+ return;
+ uint64_t unslidTargetAddress = (uint64_t)mh->preferredLoadAddress() + fixupInfo.authRebase.target;
+ resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+ resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
+ }
+#else
+ _diag.error("malformed chained pointer");
+ stop = true;
+ stopChain = true;
+#endif
+ }
+ else {
+ if ( fixupInfo.plainRebase.bind ) {
+ closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
+ if ( imageOffsetToFixup < sectVmOffset )
+ return;
+ if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+ uint64_t bindIndex = offsetIntoSection/entrySize;
+ if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+ return;
+ resolvedTuples[bindIndex].stockImplementation = bindTarget;
+ }
+ else {
+ if ( imageOffsetToFixup < sectVmOffset )
+ return;
+ if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+ return;
+ uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+ uint64_t rebaseIndex = offsetIntoSection/entrySize;
+ if ( rebaseIndex*entrySize != offsetIntoSection )
+ return;
+ uint64_t unslidTargetAddress = fixupInfo.plainRebase.signExtendedTarget();
+ resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+ resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
+ }
+ }
+ });
+ });
+
+ // remove any tuples in which both sides are not set (or target is weak-import NULL)
+ STACK_ALLOC_ARRAY(InterposingTuple, goodTuples, tupleCount);
+ for (uint32_t i=0; i < tupleCount; ++i) {
+ if ( (resolvedTuples[i].stockImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute)
+ && (resolvedTuples[i].newImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute) )
+ goodTuples.push_back(resolvedTuples[i]);
+ }
+ writer.addInterposingTuples(goodTuples);
+
+ // if the target of the interposing is in the dyld shared cache, add a PatchEntry so the cache is fixed up at launch
+ STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, goodTuples.count());
+ for (const InterposingTuple& aTuple : goodTuples) {
+ if ( aTuple.stockImplementation.sharedCache.kind == Image::ResolvedSymbolTarget::kindSharedCache ) {
+ uint32_t imageIndex;
+ assert(_dyldCache->addressInText((uint32_t)aTuple.stockImplementation.sharedCache.offset, &imageIndex));
+ ImageNum imageInCache = imageIndex+1;
+ Closure::PatchEntry patch;
+ patch.exportCacheOffset = (uint32_t)aTuple.stockImplementation.sharedCache.offset;
+ patch.overriddenDylibInCache = imageInCache;
+ patch.replacement = aTuple.newImplementation;
+ patches.push_back(patch);
+ }
+ }
+ writer.addCachePatches(patches);
+ });
+}
+
+void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh)
+{
+ const uint64_t ptrSize = mh->pointerSize();
+ Image::RebasePattern maxLeapPattern = { 0xFFFFF, 0, 0xF };
+ const uint64_t maxLeapCount = maxLeapPattern.repeatCount * maxLeapPattern.skipCount;
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern, rebaseEntries, 1024);
+ __block uint64_t lastLocation = -ptrSize;
+ mh->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
+ const uint64_t delta = runtimeOffset - lastLocation;
+ const bool aligned = ((delta % ptrSize) == 0);
+ if ( delta == ptrSize ) {
+ // this rebase location is contiguous to previous
+ if ( rebaseEntries.back().contigCount < 255 ) {
+ // just bump previous's contigCount
+ rebaseEntries.back().contigCount++;
+ }
+ else {
+ // previous contiguous run already has max 255, so start a new run
+ rebaseEntries.push_back({ 1, 1, 0 });
+ }
+ }
+ else if ( aligned && (delta <= (ptrSize*15)) ) {
+ // this rebase is within skip distance of last rebase
+ rebaseEntries.back().skipCount = (uint8_t)((delta-ptrSize)/ptrSize);
+ int lastIndex = (int)(rebaseEntries.count() - 1);
+ if ( lastIndex > 1 ) {
+ if ( (rebaseEntries[lastIndex].contigCount == rebaseEntries[lastIndex-1].contigCount)
+ && (rebaseEntries[lastIndex].skipCount == rebaseEntries[lastIndex-1].skipCount) ) {
+ // this entry as same contig and skip as prev, so remove it and bump repeat count of previous
+ rebaseEntries.pop_back();
+ rebaseEntries.back().repeatCount += 1;
+ }
+ }
+ rebaseEntries.push_back({ 1, 1, 0 });
+ }
+ else {
+ uint64_t advanceCount = (delta-ptrSize);
+ if ( (runtimeOffset < lastLocation) && (lastLocation != -ptrSize) ) {
+ // out of rebases! handle this be resting rebase offset to zero
+ rebaseEntries.push_back({ 0, 0, 0 });
+ advanceCount = runtimeOffset;
+ }
+ // if next rebase is too far to reach with one pattern, use series
+ while ( advanceCount > maxLeapCount ) {
+ rebaseEntries.push_back(maxLeapPattern);
+ advanceCount -= maxLeapCount;
+ }
+ // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
+ while ( advanceCount > maxLeapPattern.repeatCount ) {
+ uint64_t count = advanceCount / maxLeapPattern.skipCount;
+ rebaseEntries.push_back({ (uint32_t)count, 0, maxLeapPattern.skipCount });
+ advanceCount -= (count*maxLeapPattern.skipCount);
+ }
+ if ( advanceCount != 0 )
+ rebaseEntries.push_back({ (uint32_t)advanceCount, 0, 1 });
+ rebaseEntries.push_back({ 1, 1, 0 });
+ }
+ lastLocation = runtimeOffset;
+ });
+ writer.setRebaseInfo(rebaseEntries);
+
+ // i386 programs also use text relocs to rebase stubs
+ if ( mh->cputype == CPU_TYPE_I386 ) {
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern, textRebases, 512);
+ __block uint64_t lastOffset = -4;
+ mh->forEachTextRebase(_diag, ^(uint64_t runtimeOffset, bool& stop) {
+ if ( textRebases.freeCount() < 2 ) {
+ _diag.error("too many text rebase locations (%ld) in %s", textRebases.maxCount(), writer.currentImage()->path());
+ stop = true;
+ }
+ bool mergedIntoPrevious = false;
+ if ( (runtimeOffset > lastOffset) && !textRebases.empty() ) {
+ uint32_t skipAmount = (uint32_t)(runtimeOffset - lastOffset);
+ if ( (textRebases.back().repeatCount == 1) && (textRebases.back().skipCount == 0) ) {
+ textRebases.back().repeatCount = 2;
+ textRebases.back().skipCount = skipAmount;
+ mergedIntoPrevious = true;
+ }
+ else if ( textRebases.back().skipCount == skipAmount ) {
+ textRebases.back().repeatCount += 1;
+ mergedIntoPrevious = true;
+ }
+ }
+ if ( !mergedIntoPrevious ) {
+ Image::TextFixupPattern pattern;
+ pattern.target.raw = 0;
+ pattern.startVmOffset = (uint32_t)runtimeOffset;
+ pattern.repeatCount = 1;
+ pattern.skipCount = 0;
+ textRebases.push_back(pattern);
+ }
+ lastOffset = runtimeOffset;
+ });
+ writer.setTextRebaseInfo(textRebases);
+ }
+}
+
+
+void ClosureBuilder::forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
+ void (^strongHandler)(const char* strongSymbolName))
+{
+ __block int lastLibOrdinal = 256;
+ __block const char* lastSymbolName = nullptr;
+ __block uint64_t lastAddend = 0;
+ __block Image::ResolvedSymbolTarget target;
+ __block ResolvedTargetInfo targetInfo;
+ forImage.loadAddress()->forEachBind(_diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+ if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) {
+ // same symbol lookup as last location
+ handler(runtimeOffset, target, targetInfo, stop);
+ }
+ else if ( findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
+ handler(runtimeOffset, target, targetInfo, stop);
+ lastSymbolName = symbolName;
+ lastLibOrdinal = libOrdinal;
+ lastAddend = addend;
+ }
+ else {
+ stop = true;
+ }
+ }, ^(const char* symbolName) {
+ strongHandler(symbolName);
+ });
+}
+
+void ClosureBuilder::addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+ const uint32_t ptrSize = forImage.loadAddress()->pointerSize();
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern, binds, 512);
+ __block uint64_t lastOffset = -ptrSize;
+ __block Image::ResolvedSymbolTarget lastTarget = { {0, 0} };
+ forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
+ if ( targetInfo.weakBindCoalese ) {
+ // may be previous bind to this location
+ // if so, update that rather create new BindPattern
+ for (Image::BindPattern& aBind : binds) {
+ if ( (aBind.startVmOffset == runtimeOffset) && (aBind.repeatCount == 1) && (aBind.skipCount == 0) ) {
+ aBind.target = target;
+ return;
+ }
+ }
+ }
+ bool mergedIntoPrevious = false;
+ if ( !mergedIntoPrevious && (target == lastTarget) && (runtimeOffset > lastOffset) && !binds.empty() ) {
+ uint64_t skipAmount = (runtimeOffset - lastOffset - ptrSize)/ptrSize;
+ if ( skipAmount*ptrSize != (runtimeOffset - lastOffset - ptrSize) ) {
+ // misaligned pointer means we cannot optimize
+ }
+ else {
+ if ( (binds.back().repeatCount == 1) && (binds.back().skipCount == 0) && (skipAmount <= 255) ) {
+ binds.back().repeatCount = 2;
+ binds.back().skipCount = skipAmount;
+ assert(binds.back().skipCount == skipAmount); // check overflow
+ mergedIntoPrevious = true;
+ }
+ else if ( (binds.back().skipCount == skipAmount) && (binds.back().repeatCount < 0xfff) ) {
+ uint32_t prevRepeatCount = binds.back().repeatCount;
+ binds.back().repeatCount += 1;
+ assert(binds.back().repeatCount > prevRepeatCount); // check overflow
+ mergedIntoPrevious = true;
+ }
+ }
+ }
+ if ( (target == lastTarget) && (runtimeOffset == lastOffset) && !binds.empty() ) {
+ // duplicate bind for same location, ignore this one
+ mergedIntoPrevious = true;
+ }
+ if ( !mergedIntoPrevious ) {
+ Image::BindPattern pattern;
+ pattern.target = target;
+ pattern.startVmOffset = runtimeOffset;
+ pattern.repeatCount = 1;
+ pattern.skipCount = 0;
+ assert(pattern.startVmOffset == runtimeOffset);
+ binds.push_back(pattern);
+ }
+ lastTarget = target;
+ lastOffset = runtimeOffset;
+ }, ^(const char* strongSymbolName) {
+ if ( !_makingDyldCacheImages ) {
+ // something has a strong symbol definition that may override a weak impl in the dyld cache
+ Image::ResolvedSymbolTarget strongOverride;
+ ResolvedTargetInfo strongTargetInfo;
+ if ( findSymbolInImage(forImage.loadAddress(), strongSymbolName, 0, false, strongOverride, strongTargetInfo) ) {
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( li.loadAddress()->inDyldCache() && li.loadAddress()->hasWeakDefs() ) {
+ Image::ResolvedSymbolTarget implInCache;
+ ResolvedTargetInfo implInCacheInfo;
+ if ( findSymbolInImage(li.loadAddress(), strongSymbolName, 0, false, implInCache, implInCacheInfo) ) {
+ // found another instance in some dylib in dyld cache, will need to patch it
+ Closure::PatchEntry patch;
+ patch.exportCacheOffset = (uint32_t)implInCache.sharedCache.offset;
+ patch.overriddenDylibInCache = li.imageNum;
+ patch.replacement = strongOverride;
+ _weakDefCacheOverrides.push_back(patch);
+ }
+ }
+ }
+ }
+ }
+ });
+ writer.setBindInfo(binds);
+}
+
+void ClosureBuilder::reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+ // report all rebases
+ forImage.loadAddress()->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
+ _handlers->rebase(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset);
+ });
+
+ // report all binds
+ forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
+ _handlers->bind(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset, target, targetInfo);
+ },
+ ^(const char* strongSymbolName) {});
+
+ // i386 programs also use text relocs to rebase stubs
+ if ( forImage.loadAddress()->cputype == CPU_TYPE_I386 ) {
+ // FIX ME
+ }
+}
+
+// These are mangled symbols for all the variants of operator new and delete
+// which a main executable can define (non-weak) and override the
+// weak-def implementation in the OS.
+static const char* sTreatAsWeak[] = {
+ "__Znwm", "__ZnwmRKSt9nothrow_t",
+ "__Znam", "__ZnamRKSt9nothrow_t",
+ "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
+ "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
+ "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
+ "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
+ "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
+ "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
+};
+
+
+void ClosureBuilder::addChainedFixupInfo(ImageWriter& writer, const BuilderLoadedImage& forImage)
+{
+ // calculate max page starts
+ __block uint32_t dataPageCount = 1;
+ forImage.loadAddress()->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+ if ( info.protections & VM_PROT_WRITE ) {
+ dataPageCount += ((info.fileSize+4095) / 4096);
+ }
+ });
+
+ // build array of starts
+ STACK_ALLOC_ARRAY(uint64_t, starts, dataPageCount);
+ forImage.loadAddress()->forEachChainedFixupStart(_diag, ^(uint64_t runtimeOffset, bool& stop) {
+ starts.push_back(runtimeOffset);
+ });
+
+ // build array of targets
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget, targets, 1024);
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ResolvedTargetInfo, targetInfos, 1024);
+ forImage.loadAddress()->forEachChainedFixupTarget(_diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ Image::ResolvedSymbolTarget target;
+ ResolvedTargetInfo targetInfo;
+ if ( !findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
+ const char* expectedInPath = forImage.loadAddress()->dependentDylibLoadPath(libOrdinal-1);
+ _diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, forImage.path());
+ stop = true;
+ return;
+ }
+ if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+ // add if not already in array
+ bool alreadyInArray = false;
+ for (const char* sym : _weakDefsFromChainedBinds) {
+ if ( strcmp(sym, symbolName) == 0 ) {
+ alreadyInArray = true;
+ break;
+ }
+ }
+ if ( !alreadyInArray )
+ _weakDefsFromChainedBinds.push_back(symbolName);
+ }
+ targets.push_back(target);
+ targetInfos.push_back(targetInfo);
+ });
+ if ( _diag.hasError() )
+ return;
+
+ if ( _handlers != nullptr )
+ _handlers->chainedBind(forImage.imageNum, forImage.loadAddress(), starts, targets, targetInfos);
+ else
+ writer.setChainedFixups(starts, targets); // store results in Image object
+
+ // with chained fixups, main executable may define symbol that overrides weak-defs but has no fixup
+ if ( _isLaunchClosure && forImage.loadAddress()->hasWeakDefs() && forImage.loadAddress()->isMainExecutable() ) {
+ for (const char* weakSymbolName : sTreatAsWeak) {
+ Diagnostics exportDiag;
+ dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+ if ( forImage.loadAddress()->findExportedSymbol(exportDiag, weakSymbolName, foundInfo, nullptr) ) {
+ _weakDefsFromChainedBinds.push_back(weakSymbolName);
+ }
+ }
+ }
+}
+
+
+bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
+ Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
+{
+ targetInfo.foundInDylib = nullptr;
+ targetInfo.requestedSymbolName = symbolName;
+ targetInfo.addend = addend;
+ targetInfo.isWeakDef = false;
+ MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+ return (const MachOLoaded*)findDependent(mh, depIndex);
+ };
+ MachOAnalyzer::DependentToMachOLoaded finder = nullptr;
+ if ( followReExports )
+ finder = reexportFinder;
+
+ dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+ if ( macho->findExportedSymbol(_diag, symbolName, foundInfo, finder) ) {
+ const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
+ targetInfo.foundInDylib = foundInfo.foundInDylib;
+ targetInfo.foundSymbolName = foundInfo.foundSymbolName;
+ if ( foundInfo.isWeakDef )
+ targetInfo.isWeakDef = true;
+ if ( foundInfo.kind == MachOAnalyzer::FoundSymbol::Kind::absolute ) {
+ target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ target.absolute.value = foundInfo.value + addend;
+ }
+ else if ( impDylib->inDyldCache() ) {
+ target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
+ target.sharedCache.offset = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
+ }
+ else {
+ target.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ target.image.imageNum = findLoadedImage(impDylib).imageNum;
+ target.image.offset = foundInfo.value + addend;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool ClosureBuilder::findSymbol(const BuilderLoadedImage& fromImage, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend,
+ Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
+{
+ targetInfo.weakBindCoalese = false;
+ targetInfo.weakBindSameImage = false;
+ targetInfo.requestedSymbolName = symbolName;
+ targetInfo.libOrdinal = libOrdinal;
+ if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( !li.rtldLocal && findSymbolInImage(li.loadAddress(), symbolName, addend, true, target, targetInfo) )
+ return true;
+ }
+ if ( weakImport ) {
+ target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ target.absolute.value = 0;
+ return true;
+ }
+ _diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromImage.path());
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+ // to resolve weakDef coalesing, we need to search all images in order and use first definition
+ // but, if first found is a weakDef, a later non-weak def overrides that
+ bool foundWeakDefImpl = false;
+ bool foundStrongDefImpl = false;
+ bool foundImpl = false;
+ Image::ResolvedSymbolTarget aTarget;
+ ResolvedTargetInfo aTargetInfo;
+ STACK_ALLOC_ARRAY(const BuilderLoadedImage*, cachedDylibsUsingSymbol, 1024);
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ // only search images with weak-defs that were not loaded with RTLD_LOCAL
+ if ( li.loadAddress()->hasWeakDefs() && !li.rtldLocal ) {
+ if ( findSymbolInImage(li.loadAddress(), symbolName, addend, false, aTarget, aTargetInfo) ) {
+ foundImpl = true;
+ // with non-chained images, weak-defs first have a rebase to their local impl, and a weak-bind which allows earlier impls to override
+ if ( !li.loadAddress()->hasChainedFixups() && (aTargetInfo.foundInDylib == fromImage.loadAddress()) )
+ targetInfo.weakBindSameImage = true;
+ if ( aTargetInfo.isWeakDef ) {
+ // found a weakDef impl, if this is first found, set target to this
+ if ( !foundWeakDefImpl && !foundStrongDefImpl ) {
+ target = aTarget;
+ targetInfo = aTargetInfo;
+ }
+ foundWeakDefImpl = true;
+ }
+ else {
+ // found a non-weak impl, use this (unless early strong found)
+ if ( !foundStrongDefImpl ) {
+ target = aTarget;
+ targetInfo = aTargetInfo;
+ }
+ foundStrongDefImpl = true;
+ }
+ }
+ if ( foundImpl && !_makingDyldCacheImages && li.loadAddress()->inDyldCache() )
+ cachedDylibsUsingSymbol.push_back(&li);
+ }
+ }
+ // now that final target found, if any dylib in dyld cache uses that symbol name, redirect it to new target
+ if ( !cachedDylibsUsingSymbol.empty() ) {
+ for (const BuilderLoadedImage* li : cachedDylibsUsingSymbol) {
+ Image::ResolvedSymbolTarget implInCache;
+ ResolvedTargetInfo implInCacheInfo;
+ if ( findSymbolInImage(li->loadAddress(), symbolName, addend, false, implInCache, implInCacheInfo) ) {
+ if ( implInCache != target ) {
+ // found another instance in some dylib in dyld cache, will need to patch it
+ Closure::PatchEntry patch;
+ patch.exportCacheOffset = (uint32_t)implInCache.sharedCache.offset;
+ patch.overriddenDylibInCache = li->imageNum;
+ patch.replacement = target;
+ _weakDefCacheOverrides.push_back(patch);
+ }
+ }
+ }
+ }
+ targetInfo.weakBindCoalese = true;
+
+ if ( foundImpl )
+ return true;
+ _diag.error("symbol '%s' not found, expected to be weak-def coalesced", symbolName);
+ }
+ else {
+ const BuilderLoadedImage* targetLoadedImage = nullptr;
+ if ( (libOrdinal > 0) && (libOrdinal <= (int)fromImage.dependents.count()) ) {
+ ImageNum childNum = fromImage.dependents[libOrdinal - 1].imageNum();
+ if ( childNum != kMissingWeakLinkedImage ) {
+ targetLoadedImage = &findLoadedImage(childNum);
+ }
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+ targetLoadedImage = &fromImage;
+ }
+ else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+ targetLoadedImage = &_loadedImages[_mainProgLoadIndex];
+ }
+ else {
+ _diag.error("unknown special ordinal %d in %s", libOrdinal, fromImage.path());
+ return false;
+ }
+
+ if ( targetLoadedImage != nullptr ) {
+ if ( findSymbolInImage(targetLoadedImage->loadAddress(), symbolName, addend, true, target, targetInfo) )
+ return true;
+ }
+
+ if ( weakImport ) {
+ target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ target.absolute.value = 0;
+ return true;
+ }
+ const char* expectedInPath = targetLoadedImage ? targetLoadedImage->path() : "unknown";
+ _diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromImage.path());
+ if ( _launchErrorInfo != nullptr ) {
+ _launchErrorInfo->kind = DYLD_EXIT_REASON_SYMBOL_MISSING;
+ _launchErrorInfo->clientOfDylibPath = fromImage.path();
+ _launchErrorInfo->targetDylibPath = expectedInPath;
+ _launchErrorInfo->symbol = symbolName;
+ }
+ }
+ return false;
+}
+
+
+void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError)
+{
+ if ( initInfos[loadIndex].visited )
+ return;
+ initInfos[loadIndex].visited = true;
+ initInfos[loadIndex].danglingUpward = false;
+
+ if (_loadedImages[loadIndex].isBadImage) {
+ hasError = true;
+ return;
+ }
+
+ for (const Image::LinkedImage& dep : _loadedImages[loadIndex].dependents) {
+ if ( dep.imageNum() == kMissingWeakLinkedImage )
+ continue;
+ ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum());
+ uint32_t depLoadIndex = (uint32_t)_loadedImages.index(depLi);
+ if ( dep.kind() == Image::LinkKind::upward ) {
+ if ( !initInfos[depLoadIndex].visited )
+ initInfos[depLoadIndex].danglingUpward = true;
+ }
+ else {
+ depthFirstRecurseSetInitInfo(depLoadIndex, initInfos, initOrder, hasError);
+ if (hasError)
+ return;
+ }
+ }
+ initInfos[loadIndex].initOrder = initOrder++;
+}
+
+void ClosureBuilder::computeInitOrder(ImageWriter& imageWriter, uint32_t loadIndex)
+{
+ // allocate array to track initializers
+ InitInfo initInfos[_loadedImages.count()];
+ bzero(initInfos, sizeof(initInfos));
+
+ // recurse all images and build initializer list from bottom up
+ uint32_t initOrder = 1;
+ bool hasMissingDependent = false;
+ depthFirstRecurseSetInitInfo(loadIndex, initInfos, initOrder, hasMissingDependent);
+ if (hasMissingDependent) {
+ imageWriter.setInvalid();
+ return;
+ }
+
+ // any images not visited yet are are danging, force add them to end of init list
+ for (uint32_t i=0; i < (uint32_t)_loadedImages.count(); ++i) {
+ if ( !initInfos[i].visited && initInfos[i].danglingUpward ) {
+ depthFirstRecurseSetInitInfo(i, initInfos, initOrder, hasMissingDependent);
+ }
+ }
+
+ if (hasMissingDependent) {
+ imageWriter.setInvalid();
+ return;
+ }
+
+ // build array of just images with initializer
+ STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits, _loadedImages.count());
+ uint32_t index = 0;
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( initInfos[index].visited && li.hasInits ) {
+ indexOfImagesWithInits.push_back(index);
+ }
+ ++index;
+ }
+
+ // bubble sort (FIXME)
+ if ( indexOfImagesWithInits.count() > 1 ) {
+ for (uint32_t i=0; i < indexOfImagesWithInits.count()-1; ++i) {
+ for (uint32_t j=0; j < indexOfImagesWithInits.count()-i-1; ++j) {
+ if ( initInfos[indexOfImagesWithInits[j]].initOrder > initInfos[indexOfImagesWithInits[j+1]].initOrder ) {
+ uint32_t temp = indexOfImagesWithInits[j];
+ indexOfImagesWithInits[j] = indexOfImagesWithInits[j+1];
+ indexOfImagesWithInits[j+1] = temp;
+ }
+ }
+ }
+ }
+
+ // copy ImageNum of each image with initializers into array
+ ImageNum initNums[indexOfImagesWithInits.count()];
+ for (uint32_t i=0; i < indexOfImagesWithInits.count(); ++i) {
+ initNums[i] = _loadedImages[indexOfImagesWithInits[i]].imageNum;
+ }
+
+ // add to closure info
+ imageWriter.setInitsOrder(initNums, (uint32_t)indexOfImagesWithInits.count());
+}
+
+void ClosureBuilder::addCachePatchInfo(ImageWriter& imageWriter, const BuilderLoadedImage& forImage)
+{
+ assert(_handlers != nullptr);
+ _handlers->forEachExportsPatch(forImage.imageNum, ^(const CacheDylibsBindingHandlers::PatchInfo& info) {
+ assert(info.usesCount != 0);
+ imageWriter.addExportPatchInfo(info.exportCacheOffset, info.exportSymbolName, info.usesCount, info.usesArray);
+ });
+}
+
+void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter)
+{
+ // record which is libSystem
+ assert(_libSystemImageNum != 0);
+ closureWriter.setLibSystemImageNum(_libSystemImageNum);
+
+ // record which is libdyld
+ assert(_libDyldImageNum != 0);
+ Image::ResolvedSymbolTarget entryLocation;
+ ResolvedTargetInfo entryInfo;
+ if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, entryLocation, entryInfo) ) {
+ const dyld3::LibDyldEntryVector* libDyldEntry = nullptr;
+ switch ( entryLocation.image.kind ) {
+ case Image::ResolvedSymbolTarget::kindSharedCache:
+ libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)_dyldCache + entryLocation.sharedCache.offset);
+ break;
+ case Image::ResolvedSymbolTarget::kindImage:
+ libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)findLoadedImage(entryLocation.image.imageNum).loadAddress() + entryLocation.image.offset);
+ break;
+ }
+ if ( (libDyldEntry != nullptr) && (libDyldEntry->binaryFormatVersion == dyld3::closure::kFormatVersion) )
+ closureWriter.setLibDyldEntry(entryLocation);
+ else
+ _diag.error("libdyld.dylib entry vector is incompatible");
+ }
+ else {
+ _diag.error("libdyld.dylib is missing entry vector");
+ }
+
+ // record which is main executable
+ ImageNum mainProgImageNum = _loadedImages[_mainProgLoadIndex].imageNum;
+ closureWriter.setTopImageNum(mainProgImageNum);
+
+ // add entry
+ uint32_t entryOffset;
+ bool usesCRT;
+ if ( _loadedImages[_mainProgLoadIndex].loadAddress()->getEntry(entryOffset, usesCRT) ) {
+ Image::ResolvedSymbolTarget location;
+ location.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ location.image.imageNum = mainProgImageNum;
+ location.image.offset = entryOffset;
+ if ( usesCRT )
+ closureWriter.setStartEntry(location);
+ else
+ closureWriter.setMainEntry(location);
+ }
+
+ // add env vars that must match at launch time
+ _pathOverrides.forEachEnvVar(^(const char* envVar) {
+ closureWriter.addEnvVar(envVar);
+ });
+
+ // add list of files which must be missing
+ STACK_ALLOC_ARRAY(const char*, paths, 8192);
+ if ( _mustBeMissingPaths != nullptr ) {
+ _mustBeMissingPaths->forEachPath(^(const char* aPath) {
+ paths.push_back(aPath);
+ });
+ }
+ closureWriter.setMustBeMissingFiles(paths);
+}
+
+
+// used at launch by dyld when kernel has already mapped main executable
+const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures)
+{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
+ const mach_header* mainMH = (const mach_header*)fileInfo.fileContent;
+ // set up stack based storage for all arrays
+ BuilderLoadedImage loadImagesStorage[512];
+ Image::LinkedImage dependenciesStorage[512*8];
+ InterposingTuple tuplesStorage[64];
+ Closure::PatchEntry cachePatchStorage[64];
+ const char* weakDefNameStorage[64];
+ _loadedImages.setInitialStorage(loadImagesStorage, 512);
+ _dependencies.setInitialStorage(dependenciesStorage, 512*8);
+ _interposingTuples.setInitialStorage(tuplesStorage, 64);
+ _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
+ _weakDefsFromChainedBinds.setInitialStorage(weakDefNameStorage, 64);
+ ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
+
+ const MachOAnalyzer* mainExecutable = MachOAnalyzer::validMainExecutable(_diag, mainMH, fileInfo.path, fileInfo.sliceLen, _archName, _platform);
+ if ( mainExecutable == nullptr )
+ return nullptr;
+ if ( !mainExecutable->isDynamicExecutable() ) {
+ _diag.error("not a main executable");
+ return nullptr;
+ }
+ _isLaunchClosure = true;
+
+ // add any DYLD_INSERT_LIBRARIES
+ _nextIndex = 0;
+ _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
+ BuilderLoadedImage insertEntry;
+ insertEntry.loadedFileInfo.path = strdup_temp(dylibPath);
+ insertEntry.imageNum = _startImageNum + _nextIndex++;
+ insertEntry.unmapWhenDone = true;
+ insertEntry.contentRebased = false;
+ insertEntry.hasInits = false;
+ insertEntry.markNeverUnload = true;
+ insertEntry.rtldLocal = false;
+ insertEntry.isBadImage = false;
+ insertEntry.overrideImageNum = 0;
+ _loadedImages.push_back(insertEntry);
+ });
+ _mainProgLoadIndex = (uint32_t)_loadedImages.count();
+
+ // add main executable
+ BuilderLoadedImage mainEntry;
+ mainEntry.loadedFileInfo = fileInfo;
+ mainEntry.imageNum = _startImageNum + _nextIndex++;
+ mainEntry.unmapWhenDone = false;
+ mainEntry.contentRebased = false;
+ mainEntry.hasInits = false;
+ mainEntry.markNeverUnload = true;
+ mainEntry.rtldLocal = false;
+ mainEntry.isBadImage = false;
+ mainEntry.overrideImageNum = 0;
+ _loadedImages.push_back(mainEntry);
+
+ // get mach_headers for all images needed to launch this main executable
+ LoadedImageChain chainStart = { nullptr, _loadedImages[_mainProgLoadIndex] };
+ recursiveLoadDependents(chainStart);
+ if ( _diag.hasError() )
+ return nullptr;
+ for (uint32_t i=0; i < _mainProgLoadIndex; ++i) {
+ closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, _loadedImages[i].loadedFileInfo.path, _archName, _platform);
+ const char* originalLoadPath = _loadedImages[i].loadedFileInfo.path;
+ _loadedImages[i].loadedFileInfo = loadedFileInfo;
+ if ( _loadedImages[i].loadAddress() != nullptr ) {
+ LoadedImageChain insertChainStart = { nullptr, _loadedImages[i] };
+ recursiveLoadDependents(insertChainStart);
+ }
+ if ( _diag.hasError() || (_loadedImages[i].loadAddress() == nullptr) ) {
+ if ( !allowInsertFailures ) {
+ if ( _diag.noError() )
+ _diag.error("could not load inserted dylib %s", originalLoadPath);
+ return nullptr;
+ }
+ _diag.clearError(); // FIXME add way to plumb back warning
+ // remove slot for inserted image that could not loaded
+ _loadedImages.remove(i);
+ i -= 1;
+ _mainProgLoadIndex -= 1;
+ _nextIndex -= 1;
+ // renumber images in this closure
+ for (uint32_t j=i+1; j < _loadedImages.count(); ++j) {
+ if ( (_loadedImages[j].imageNum >= _startImageNum) && (_loadedImages[j].imageNum <= _startImageNum+_nextIndex) )
+ _loadedImages[j].imageNum -= 1;
+ }
+ }
+ }
+ loadDanglingUpwardLinks();
+
+ // only some images need to go into closure (ones from dyld cache do not)
+ STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.imageNum >= _startImageNum ) {
+ writers.push_back(ImageWriter());
+ buildImage(writers.back(), li);
+ if ( _diag.hasError() )
+ return nullptr;
+ }
+ if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
+ _libDyldImageNum = li.imageNum;
+ else if ( strcmp(li.path(), "/usr/lib/libSystem.B.dylib") == 0 )
+ _libSystemImageNum = li.imageNum;
+ }
+
+ // add initializer order into top level Images (may be inserted dylibs before main executable)
+ for (uint32_t i=0; i <= _mainProgLoadIndex; ++i)
+ computeInitOrder(writers[i], i);
+
+ // combine all Image objects into one ImageArray
+ ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+ for (ImageWriter& writer : writers) {
+ imageArrayWriter.appendImage(writer.finalize());
+ writer.deallocate();
+ }
+ const ImageArray* imageArray = imageArrayWriter.finalize();
+
+ // merge ImageArray object into LaunchClosure object
+ __block LaunchClosureWriter closureWriter(imageArray);
+
+ // record shared cache info
+ if ( _dyldCache != nullptr ) {
+ // record cache UUID
+ uuid_t cacheUUID;
+ _dyldCache->getUUID(cacheUUID);
+ closureWriter.setDyldCacheUUID(cacheUUID);
+
+ // record any cache patching needed because of dylib overriding cache
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( li.overrideImageNum != 0 ) {
+ const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
+ STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
+ //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
+ cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
+ dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+ Diagnostics patchDiag;
+ Closure::PatchEntry patch;
+ patch.overriddenDylibInCache = li.overrideImageNum;
+ patch.exportCacheOffset = cacheOffsetOfImpl;
+ if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) {
+ patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ patch.replacement.image.imageNum = li.imageNum;
+ patch.replacement.image.offset = foundInfo.value;
+ }
+ else {
+ // this means the symbol is missing in the cache override dylib, so set any uses to NULL
+ patch.replacement.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ patch.replacement.absolute.value = 0;
+ }
+ patches.push_back(patch);
+ });
+ closureWriter.addCachePatches(patches);
+ }
+ }
+
+ // handle any extra weak-def coalescing needed by chained fixups
+ if ( !_weakDefsFromChainedBinds.empty() ) {
+ for (const char* symbolName : _weakDefsFromChainedBinds) {
+ Image::ResolvedSymbolTarget cacheOverrideTarget;
+ bool haveCacheOverride = false;
+ bool foundCachOverrideIsWeakDef = false;
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( !li.loadAddress()->hasWeakDefs() )
+ continue;
+ Image::ResolvedSymbolTarget target;
+ ResolvedTargetInfo targetInfo;
+ if ( findSymbolInImage(li.loadAddress(), symbolName, 0, false, target, targetInfo) ) {
+ if ( li.loadAddress()->inDyldCache() ) {
+ if ( haveCacheOverride ) {
+ Closure::PatchEntry patch;
+ patch.exportCacheOffset = (uint32_t)target.sharedCache.offset;
+ patch.overriddenDylibInCache = li.imageNum;
+ patch.replacement = cacheOverrideTarget;
+ _weakDefCacheOverrides.push_back(patch);
+ }
+ else {
+ // found first in cached dylib, so no need to patch cache for this symbol
+ break;
+ }
+ }
+ else {
+ // found image that exports this symbol and is not in cache
+ if ( !haveCacheOverride || (foundCachOverrideIsWeakDef && !targetInfo.isWeakDef) ) {
+ // update cache to use this symbol if it if first found or it is first non-weak found
+ cacheOverrideTarget = target;
+ foundCachOverrideIsWeakDef = targetInfo.isWeakDef;
+ haveCacheOverride = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // record any cache patching needed because weak-def C++ symbols override dyld cache
+ if ( !_weakDefCacheOverrides.empty() )
+ closureWriter.addCachePatches(_weakDefCacheOverrides);
+
+ }
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // if closure is built on-device for iOS, then record boot UUID
+ char bootSessionUUID[256] = { 0 };
+ size_t bootSize = sizeof(bootSessionUUID);
+ if ( sysctlbyname("kern.bootsessionuuid", bootSessionUUID, &bootSize, NULL, 0) == 0 )
+ closureWriter.setBootUUID(bootSessionUUID);
+#endif
+
+ // record any interposing info
+ imageArray->forEachImage(^(const Image* image, bool &stop) {
+ if ( !image->inDyldCache() )
+ addInterposingTuples(closureWriter, image, findLoadedImage(image->imageNum()).loadAddress());
+ });
+
+ // modify fixups in contained Images by applying interposing tuples
+ closureWriter.applyInterposing();
+
+ // set flags
+ closureWriter.setUsedAtPaths(_atPathUsed);
+ closureWriter.setUsedFallbackPaths(_fallbackPathUsed);
+ closureWriter.setInitImageCount((uint32_t)_loadedImages.count());
+
+ // add other closure attributes
+ addClosureInfo(closureWriter);
+
+ // make result
+ const LaunchClosure* result = closureWriter.finalize();
+ imageArrayWriter.deallocate();
+
+ return result;
+}
+
+// used by libdyld for dlopen()
+const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const LaunchClosure* mainClosure, const Array<LoadedImage>& alreadyLoadedList,
+ closure::ImageNum callerImageNum, bool noLoad, bool canUseSharedCacheClosure, closure::ImageNum* topImageNum)
+{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
+ // set up stack based storage for all arrays
+ BuilderLoadedImage loadImagesStorage[512];
+ Image::LinkedImage dependenciesStorage[512*8];
+ Closure::PatchEntry cachePatchStorage[64];
+ _loadedImages.setInitialStorage(loadImagesStorage, 512);
+ _dependencies.setInitialStorage(dependenciesStorage, 512*8);
+ _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
+ ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
+
+ // fill in builder array from already loaded images
+ bool cachedDylibsExpectedOnDisk = _dyldCache ? _dyldCache->header.dylibsExpectedOnDisk : true;
+ uintptr_t callerImageIndex = UINTPTR_MAX;
+ for (const LoadedImage& ali : alreadyLoadedList) {
+ const Image* image = ali.image();
+ const MachOAnalyzer* ma = (MachOAnalyzer*)(ali.loadedAddress());
+ bool inDyldCache = ma->inDyldCache();
+ BuilderLoadedImage entry;
+ ImageNum overrideImageNum;
+ entry.loadedFileInfo.path = image->path();
+ entry.loadedFileInfo.fileContent = ma;
+ entry.loadedFileInfo.sliceOffset = 0;
+ entry.loadedFileInfo.inode = 0;
+ entry.loadedFileInfo.mtime = 0;
+ entry.imageNum = image->imageNum();
+ entry.dependents = image->dependentsArray();
+ entry.unmapWhenDone = false;
+ entry.contentRebased = inDyldCache;
+ entry.hasInits = false;
+ entry.markNeverUnload = image->neverUnload();
+ entry.rtldLocal = ali.hideFromFlatSearch();
+ entry.isBadImage = false;
+ entry.overrideImageNum = 0;
+ if ( !inDyldCache && image->isOverrideOfDyldCacheImage(overrideImageNum) ) {
+ entry.overrideImageNum = overrideImageNum;
+ canUseSharedCacheClosure = false;
+ }
+ if ( !inDyldCache || cachedDylibsExpectedOnDisk )
+ image->hasFileModTimeAndInode(entry.loadedFileInfo.inode, entry.loadedFileInfo.mtime);
+ if ( entry.imageNum == callerImageNum )
+ callerImageIndex = _loadedImages.count();
+ _loadedImages.push_back(entry);
+ }
+ _alreadyInitedIndex = (uint32_t)_loadedImages.count();
+
+ // find main executable (may be needed for @executable_path)
+ _isLaunchClosure = false;
+ for (uint32_t i=0; i < alreadyLoadedList.count(); ++i) {
+ if ( _loadedImages[i].loadAddress()->isMainExecutable() ) {
+ _mainProgLoadIndex = i;
+ break;
+ }
+ }
+
+ // add top level dylib being dlopen()ed
+ BuilderLoadedImage* foundTopImage;
+ _nextIndex = 0;
+ // @rpath has caller's LC_PRATH, then main executable's LC_RPATH
+ BuilderLoadedImage& callerImage = (callerImageIndex != UINTPTR_MAX) ? _loadedImages[callerImageIndex] : _loadedImages[_mainProgLoadIndex];
+ LoadedImageChain chainCaller = { nullptr, callerImage };
+ LoadedImageChain chainMain = { &chainCaller, _loadedImages[_mainProgLoadIndex] };
+ if ( !findImage(path, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
+ // If we didn't find the image, but its a shared cache path, then try again with realpath.
+ if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) {
+ char resolvedPath[PATH_MAX];
+ if ( _fileSystem.getRealPath(path, resolvedPath) ) {
+ if ( !findImage(resolvedPath, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
+ return nullptr;
+ }
+ } else {
+ // We didn't find a new path from realpath
+ return nullptr;
+ }
+ } else {
+ // Not in /usr/lib/ or /System/Library/
+ return nullptr;
+ }
+ }
+
+ // exit early in RTLD_NOLOAD mode
+ if ( noLoad ) {
+ // if no new images added to _loadedImages, then requested path was already loaded
+ if ( (uint32_t)_loadedImages.count() == _alreadyInitedIndex )
+ *topImageNum = foundTopImage->imageNum;
+ else
+ *topImageNum = 0;
+ return nullptr;
+ }
+
+ // fast path if roots are not allowed and target is in dyld cache or is other
+ if ( (_dyldCache != nullptr) && (_dyldCache->header.cacheType == kDyldSharedCacheTypeProduction) ) {
+ if ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) {
+ *topImageNum = foundTopImage->imageNum;
+ return nullptr;
+ }
+ }
+
+ // recursive load dependents
+ // @rpath for stuff top dylib depends on uses LC_RPATH from caller, main exe, and dylib being dlopen()ed
+ LoadedImageChain chainTopDylib = { &chainMain, *foundTopImage };
+ recursiveLoadDependents(chainTopDylib);
+ if ( _diag.hasError() )
+ return nullptr;
+ loadDanglingUpwardLinks();
+
+ // only some images need to go into closure (ones from dyld cache do not)
+ STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.imageNum >= _startImageNum ) {
+ writers.push_back(ImageWriter());
+ buildImage(writers.back(), li);
+ }
+ }
+
+ // check if top image loaded is in shared cache along with everything it depends on
+ *topImageNum = foundTopImage->imageNum;
+ if ( writers.count() == 0 ) {
+ return nullptr;
+ } else if ( canUseSharedCacheClosure && ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) ) {
+ // We used a shared cache built closure, but now discovered roots. We need to try again
+ topImageNum = 0;
+ return sRetryDlopenClosure;
+ }
+
+ // add initializer order into top level Image
+ computeInitOrder(writers[0], (uint32_t)alreadyLoadedList.count());
+
+ // combine all Image objects into one ImageArray
+ ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+ for (ImageWriter& writer : writers) {
+ imageArrayWriter.appendImage(writer.finalize());
+ writer.deallocate();
+ }
+ const ImageArray* imageArray = imageArrayWriter.finalize();
+
+ // merge ImageArray object into LaunchClosure object
+ DlopenClosureWriter closureWriter(imageArray);
+
+ // add other closure attributes
+ closureWriter.setTopImageNum(foundTopImage->imageNum);
+
+ // record any cache patching needed because of dylib overriding cache
+ if ( _dyldCache != nullptr ) {
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( (li.overrideImageNum != 0) && (li.imageNum >= _startImageNum) ) {
+ const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
+ STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
+ //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
+ cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
+ dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+ Diagnostics patchDiag;
+ Closure::PatchEntry patch;
+ patch.overriddenDylibInCache = li.overrideImageNum;
+ patch.exportCacheOffset = cacheOffsetOfImpl;
+ if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) {
+ patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ patch.replacement.image.imageNum = li.imageNum;
+ patch.replacement.image.offset = foundInfo.value;
+ }
+ else {
+ patch.replacement.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
+ patch.replacement.absolute.value = 0;
+ }
+ patches.push_back(patch);
+ });
+ closureWriter.addCachePatches(patches);
+ }
+ }
+ }
+
+ // Dlopen's should never keep track of missing paths as we don't cache these closures.
+ assert(_mustBeMissingPaths == nullptr);
+
+ // make final DlopenClosure object
+ const DlopenClosure* result = closureWriter.finalize();
+ imageArrayWriter.deallocate();
+ return result;
+}
+
+
+// used by dyld_closure_util
+const LaunchClosure* ClosureBuilder::makeLaunchClosure(const char* mainPath, bool allowInsertFailures)
+{
+ closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, mainPath, _archName, _platform);
+ const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+ loadedFileInfo.path = mainPath;
+ if (_diag.hasError())
+ return nullptr;
+ if (mh == nullptr) {
+ _diag.error("could not load file");
+ return nullptr;
+ }
+ if (!mh->isDynamicExecutable()) {
+ _diag.error("file is not an executable");
+ return nullptr;
+ }
+ const_cast<PathOverrides*>(&_pathOverrides)->setMainExecutable(mh, mainPath);
+ const LaunchClosure* launchClosure = makeLaunchClosure(loadedFileInfo, allowInsertFailures);
+ loadedFileInfo.unload(loadedFileInfo);
+ return launchClosure;
+}
+
+
+// used by dyld shared cache builder
+const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases)
+{
+ // because this is run in cache builder using dispatch_apply() there is minimal stack space
+ // so set up storage for all arrays to be vm_allocated
+ uintptr_t maxImageCount = dylibs.count() + 16;
+ _loadedImages.reserve(maxImageCount);
+ _dependencies.reserve(maxImageCount*16);
+
+ _makingDyldCacheImages = true;
+ _makingCustomerCache = customerCache;
+ _aliases = &aliases;
+
+ // build _loadedImages[] with every dylib in cache
+ __block ImageNum imageNum = _startImageNum;
+ for (const CachedDylibInfo& aDylibInfo : dylibs) {
+ BuilderLoadedImage entry;
+ entry.loadedFileInfo = aDylibInfo.fileInfo;
+ entry.imageNum = imageNum++;
+ entry.unmapWhenDone = false;
+ entry.contentRebased = false;
+ entry.hasInits = false;
+ entry.markNeverUnload = true;
+ entry.rtldLocal = false;
+ entry.isBadImage = false;
+ entry.overrideImageNum = 0;
+ _loadedImages.push_back(entry);
+ }
+
+ // wire up dependencies between cached dylibs
+ for (BuilderLoadedImage& li : _loadedImages) {
+ LoadedImageChain chainStart = { nullptr, li };
+ recursiveLoadDependents(chainStart);
+ if ( _diag.hasError() )
+ break;
+ }
+ assert(_loadedImages.count() == dylibs.count());
+
+ // create an ImageWriter for each cached dylib
+ STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+ for (BuilderLoadedImage& li : _loadedImages) {
+ writers.push_back(ImageWriter());
+ buildImage(writers.back(), li);
+ }
+
+ // add initializer order into each dylib
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ uint32_t index = li.imageNum - _startImageNum;
+ computeInitOrder(writers[index], index);
+ }
+
+ // add exports patch info for each dylib
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ uint32_t index = li.imageNum - _startImageNum;
+ addCachePatchInfo(writers[index], li);
+ }
+
+ // combine all Image objects into one ImageArray
+ ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+ for (ImageWriter& writer : writers) {
+ imageArrayWriter.appendImage(writer.finalize());
+ writer.deallocate();
+ }
+ const ImageArray* imageArray = imageArrayWriter.finalize();
+
+ return imageArray;
+}
+
+
+#if BUILDING_CACHE_BUILDER
+const ImageArray* ClosureBuilder::makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount)
+{
+ // because this is run in cache builder using dispatch_apply() there is minimal stack space
+ // so set up storage for all arrays to be vm_allocated
+ uintptr_t maxImageCount = otherDylibs.count() + cachedDylibsCount + 128;
+ _loadedImages.reserve(maxImageCount);
+ _dependencies.reserve(maxImageCount*16);
+
+ // build _loadedImages[] with every dylib in cache, followed by others
+ _nextIndex = 0;
+ for (const LoadedFileInfo& aDylibInfo : otherDylibs) {
+ BuilderLoadedImage entry;
+ entry.loadedFileInfo = aDylibInfo;
+ entry.imageNum = _startImageNum + _nextIndex++;
+ entry.unmapWhenDone = false;
+ entry.contentRebased = false;
+ entry.hasInits = false;
+ entry.markNeverUnload = false;
+ entry.rtldLocal = false;
+ entry.isBadImage = false;
+ entry.overrideImageNum = 0;
+ _loadedImages.push_back(entry);
+ }
+
+ // wire up dependencies between cached dylibs
+ // Note, _loadedImages can grow when we call recursiveLoadDependents so we need
+ // to check the count on each iteration.
+ for (uint64_t index = 0; index != _loadedImages.count(); ++index) {
+ BuilderLoadedImage& li = _loadedImages[index];
+ LoadedImageChain chainStart = { nullptr, li };
+ recursiveLoadDependents(chainStart);
+ if ( _diag.hasError() ) {
+ _diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+ //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+ _diag.clearError();
+ li.isBadImage = true; // mark bad
+ }
+ }
+
+ auto invalidateBadImages = [&]() {
+ // Invalidate images with bad dependencies
+ while (true) {
+ bool madeChange = false;
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if (li.isBadImage) {
+ // Already invalidated
+ continue;
+ }
+ for (Image::LinkedImage depIndex : li.dependents) {
+ if ( depIndex.imageNum() == kMissingWeakLinkedImage )
+ continue;
+ if ( depIndex.imageNum() < dyld3::closure::kLastDyldCacheImageNum )
+ continue;
+ BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum());
+ if (depImage.isBadImage) {
+ _diag.warning("while building dlopen closure for %s: dependent dylib had error", li.loadedFileInfo.path);
+ li.isBadImage = true; // mark bad
+ madeChange = true;
+ }
+ }
+ }
+ if (!madeChange)
+ break;
+ }
+ };
+
+ invalidateBadImages();
+
+ // create an ImageWriter for each cached dylib
+ STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+ for (BuilderLoadedImage& li : _loadedImages) {
+ if ( li.imageNum == 0 ) {
+ writers.push_back(ImageWriter());
+ writers.back().setInvalid();
+ continue;
+ }
+ if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
+ continue;
+ writers.push_back(ImageWriter());
+ buildImage(writers.back(), li);
+ if ( _diag.hasError() ) {
+ _diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+ //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+ _diag.clearError();
+ li.isBadImage = true; // mark bad
+ writers.back().setInvalid();
+ }
+ }
+
+ invalidateBadImages();
+
+ // add initializer order into each dylib
+ for (const BuilderLoadedImage& li : _loadedImages) {
+ if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
+ continue;
+ if (li.isBadImage)
+ continue;
+ uint32_t index = li.imageNum - _startImageNum;
+ computeInitOrder(writers[index], index);
+ }
+
+ // combine all Image objects into one ImageArray
+ ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+ for (ImageWriter& writer : writers) {
+ imageArrayWriter.appendImage(writer.finalize());
+ writer.deallocate();
+ }
+ const ImageArray* imageArray = imageArrayWriter.finalize();
+
+ return imageArray;
+}
+#endif
+
+
+bool ClosureBuilder::inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum)
+{
+ for (const LoadedImage& ali : loadedList) {
+ if ( ali.image()->representsImageNum(imageNum) )
+ return true;
+ }
+ return false;
+}
+
+void ClosureBuilder::buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* image)
+{
+ // breadth first load
+ STACK_ALLOC_ARRAY(const Image*, needToRecurse, 256);
+ image->forEachDependentImage(^(uint32_t dependentIndex, dyld3::closure::Image::LinkKind kind, ImageNum depImageNum, bool &stop) {
+ if ( !inLoadedImageArray(loadedList, depImageNum) ) {
+ const Image* depImage = ImageArray::findImage(imagesArrays, depImageNum);
+ loadedList.push_back(LoadedImage::make(depImage));
+ needToRecurse.push_back(depImage);
+ }
+ });
+
+ // recurse load
+ for (const Image* img : needToRecurse) {
+ buildLoadOrderRecurse(loadedList, imagesArrays, img);
+ }
+}
+
+void ClosureBuilder::buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd)
+{
+ const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImage());
+ loadedList.push_back(LoadedImage::make(topImage));
+ buildLoadOrderRecurse(loadedList, imagesArrays, topImage);
+}
+
+
+
+} // namespace closure
+} // 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 ClosureBuilder_h
+#define ClosureBuilder_h
+
+
+#include "Closure.h"
+#include "ClosureFileSystem.h"
+#include "ClosureWriter.h"
+#include "PathOverrides.h"
+#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "Loading.h"
+
+
+
+namespace dyld3 {
+
+
+namespace closure {
+
+
+
+class VIS_HIDDEN ClosureBuilder
+{
+public:
+
+ struct LaunchErrorInfo
+ {
+ uintptr_t kind;
+ const char* clientOfDylibPath;
+ const char* targetDylibPath;
+ const char* symbol;
+ };
+
+ struct ResolvedTargetInfo
+ {
+ const MachOLoaded* foundInDylib;
+ const char* requestedSymbolName;
+ const char* foundSymbolName;
+ uint64_t addend;
+ bool weakBindCoalese;
+ bool weakBindSameImage;
+ bool isWeakDef;
+ int libOrdinal;
+ };
+
+ typedef Image::PatchableExport::PatchLocation PatchLocation;
+
+ struct CacheDylibsBindingHandlers
+ {
+ struct PatchInfo
+ {
+ const char* exportSymbolName;
+ uint32_t exportCacheOffset;
+ uint32_t usesCount;
+ const PatchLocation* usesArray;
+ };
+
+ void (^rebase)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset);
+ void (^bind)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo);
+ void (^chainedBind)(ImageNum, const MachOLoaded*, const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets, const Array<ResolvedTargetInfo>& targetInfos);
+ void (^forEachExportsPatch)(ImageNum, void (^handler)(const PatchInfo&));
+ };
+
+ enum class AtPath { none, all, onlyInRPaths };
+
+ ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+ const PathOverrides& pathOverrides, AtPath atPathHandling=AtPath::all,
+ LaunchErrorInfo* errorInfo=nullptr,
+ const char* archName=MachOFile::currentArchName(), Platform platform=MachOFile::currentPlatform(),
+ const CacheDylibsBindingHandlers* handlers=nullptr);
+ ~ClosureBuilder();
+ Diagnostics& diagnostics() { return _diag; }
+
+ const LaunchClosure* makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures);
+
+ const LaunchClosure* makeLaunchClosure(const char* mainPath,bool allowInsertFailures);
+
+
+ static const DlopenClosure* sRetryDlopenClosure;
+ const DlopenClosure* makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array<LoadedImage>& loadedList,
+ closure::ImageNum callerImageNum, bool noLoad, bool canUseSharedCacheClosure,
+ closure::ImageNum* topImageNum);
+
+ ImageNum nextFreeImageNum() const { return _startImageNum + _nextIndex; }
+
+
+ struct PatchableExport
+ {
+ uint32_t cacheOffsetOfImpl;
+ uint32_t cacheOffsetOfName;
+ uint32_t patchLocationsCount;
+ const PatchLocation* patchLocations;
+ };
+
+ struct CachedDylibInfo
+ {
+ LoadedFileInfo fileInfo;
+ };
+
+ struct CachedDylibAlias
+ {
+ const char* realPath;
+ const char* aliasPath;
+ };
+
+ const ImageArray* makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases);
+
+ const ImageArray* makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount);
+
+ static void buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd);
+
+private:
+
+
+ struct InitInfo
+ {
+ uint32_t initOrder;
+ bool danglingUpward;
+ bool visited;
+ };
+
+ struct BuilderLoadedImage
+ {
+ Array<Image::LinkedImage> dependents;
+ ImageNum imageNum;
+ uint32_t unmapWhenDone : 1,
+ contentRebased : 1,
+ hasInits : 1,
+ markNeverUnload : 1,
+ rtldLocal : 1,
+ isBadImage : 1,
+ padding : 14,
+ overrideImageNum : 12;
+ LoadedFileInfo loadedFileInfo;
+
+ // Convenience method to get the information from the loadedFileInfo
+ const MachOAnalyzer* loadAddress() const { return (const MachOAnalyzer*)loadedFileInfo.fileContent; }
+ const char* path() const { return loadedFileInfo.path; }
+ };
+
+ struct LoadedImageChain
+ {
+ LoadedImageChain* previous;
+ BuilderLoadedImage& image;
+ };
+
+
+ void recursiveLoadDependents(LoadedImageChain& forImageChain);
+ void loadDanglingUpwardLinks();
+ const char* resolvePathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath);
+ bool findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, bool mustBeDylib, bool allowOther=true);
+ void buildImage(ImageWriter& writer, BuilderLoadedImage& forImage);
+ void addSegments(ImageWriter& writer, const MachOAnalyzer* mh);
+ void addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+ void addSynthesizedRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+ void addSynthesizedBindInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+ void addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
+ void reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage);
+ void addChainedFixupInfo(ImageWriter& writer, const BuilderLoadedImage& forImage);
+ void addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh);
+ void computeInitOrder(ImageWriter& writer, uint32_t loadIndex);
+ void addCachePatchInfo(ImageWriter& writer, const BuilderLoadedImage& forImage);
+ void addClosureInfo(LaunchClosureWriter& closureWriter);
+ void depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError);
+ bool findSymbol(const BuilderLoadedImage& fromImage, int libraryOrdinal, const char* symbolName, bool weakImport, uint64_t addend,
+ Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
+ bool findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
+ const MachOAnalyzer* machOForImageNum(ImageNum imageNum);
+ ImageNum imageNumForMachO(const MachOAnalyzer* mh);
+ const MachOAnalyzer* findDependent(const MachOLoaded* mh, uint32_t depIndex);
+ BuilderLoadedImage& findLoadedImage(ImageNum imageNum);
+ BuilderLoadedImage& findLoadedImage(const MachOAnalyzer* mh);
+ uint32_t index(const BuilderLoadedImage&);
+ bool expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[]);
+ bool expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[]);
+ void addMustBeMissingPath(const char* path);
+ const char* strdup_temp(const char* path);
+ bool overridableDylib(const BuilderLoadedImage& forImage);
+ void forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
+ void (^strongHandler)(const char* strongSymbolName));
+
+ static bool inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum);
+ static void buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* toAdd);
+
+ const FileSystem& _fileSystem;
+ const DyldSharedCache* const _dyldCache;
+ const PathOverrides& _pathOverrides;
+ const char* const _archName;
+ Platform const _platform;
+ uint32_t const _startImageNum;
+ const ImageArray* _dyldImageArray = nullptr;
+ const CacheDylibsBindingHandlers* _handlers = nullptr;
+ const Array<CachedDylibAlias>* _aliases = nullptr;
+ const AtPath _atPathHandling = AtPath::none;
+ uint32_t _mainProgLoadIndex = 0;
+ Diagnostics _diag;
+ LaunchErrorInfo* _launchErrorInfo = nullptr;
+ PathPool* _tempPaths = nullptr;
+ PathPool* _mustBeMissingPaths = nullptr;
+ uint32_t _nextIndex = 0;
+ OverflowSafeArray<BuilderLoadedImage> _loadedImages;
+ OverflowSafeArray<Image::LinkedImage,65536> _dependencies; // all dylibs in cache need ~20,000 edges
+ OverflowSafeArray<InterposingTuple> _interposingTuples;
+ OverflowSafeArray<Closure::PatchEntry> _weakDefCacheOverrides;
+ OverflowSafeArray<const char*> _weakDefsFromChainedBinds;
+ uint32_t _alreadyInitedIndex = 0;
+ bool _isLaunchClosure = false;
+ bool _makingDyldCacheImages = false;
+ bool _dyldCacheIsLive = false; // means kernel is rebasing dyld cache content being viewed
+ bool _makingClosuresInCache = false;
+ bool _makingCustomerCache = false;
+ bool _atPathUsed = false;
+ bool _fallbackPathUsed = false;
+ ImageNum _libDyldImageNum = 0;
+ ImageNum _libSystemImageNum = 0;
+};
+
+
+
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+#endif /* ClosureBuilder_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 ClosureFileSystem_h
+#define ClosureFileSystem_h
+
+// For MAXPATHLEN
+#include <sys/param.h>
+// For va_list
+#include <stdarg.h>
+// For uint64_t
+#include <stdint.h>
+
+namespace dyld3 {
+namespace closure {
+
+struct LoadedFileInfo {
+ const void* fileContent = nullptr;
+ uint64_t fileContentLen = 0;
+ uint64_t sliceOffset = 0;
+ uint64_t sliceLen = 0;
+ uint64_t inode = 0;
+ uint64_t mtime = 0;
+ void (*unload)(const LoadedFileInfo&) = nullptr;
+ const char* path = nullptr;
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+class FileSystem {
+protected:
+ FileSystem() { }
+
+public:
+
+ // Get the real path for a given path, if it exists.
+ // Returns true if the real path was found and updates the given buffer iff that is the case
+ virtual bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const = 0;
+
+ // Returns true on success. If an error occurs the given callback will be called with the reason.
+ // On success, info is filled with info about the loaded file. If the path supplied includes a symlink,
+ // the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
+ virtual bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const = 0;
+
+ // Frees the buffer allocated by loadFile()
+ virtual void unloadFile(const LoadedFileInfo& info) const = 0;
+
+ // Frees all but the requested range and adjusts info to new buffer location
+ // Remaining buffer can be freed later with unloadFile()
+ virtual void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const = 0;
+
+ // If a file exists at path, returns true and sets inode and mtime
+ virtual bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const = 0;
+};
+#pragma clang diagnostic pop
+
+} // namespace closure
+} // namespace dyld3
+
+#endif /* ClosureFileSystem_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 "ClosureFileSystemPhysical.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sandbox.h>
+#include <sandbox/private.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+using dyld3::closure::FileSystemPhysical;
+
+bool FileSystemPhysical::getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const {
+ bool success = false;
+ int fd = ::open(possiblePath, O_RDONLY);
+ if ( fd != -1 ) {
+ success = fcntl(fd, F_GETPATH, realPath) == 0;
+ ::close(fd);
+ }
+ if (success)
+ return success;
+ realpath(possiblePath, realPath);
+ int realpathErrno = errno;
+ // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+ return (realpathErrno == ENOENT) || (realpathErrno == 0);
+}
+
+static bool sandboxBlocked(const char* path, const char* kind)
+{
+#if TARGET_IPHONE_SIMULATOR
+ // sandbox calls not yet supported in dyld_sim
+ return false;
+#else
+ sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+ return ( sandbox_check(getpid(), kind, filter, path) > 0 );
+#endif
+}
+
+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");
+}
+
+// Returns true on success. If an error occurs the given callback will be called with the reason.
+// On success, info is filled with info about the loaded file. If the path supplied includes a symlink,
+// the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
+bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const {
+ // open file
+ const char* originalPath = path;
+ char altPath[PATH_MAX];
+ int fd = -1;
+ if ( _fileSystemPrefix != nullptr ) {
+ strlcpy(altPath, _fileSystemPrefix, PATH_MAX);
+ strlcat(altPath, path, PATH_MAX);
+ fd = ::open(altPath, O_RDONLY, 0);
+ if ( fd != -1 )
+ path = altPath;
+ }
+ if ( fd == -1 ) {
+ fd = ::open(path, O_RDONLY, 0);
+ if ( fd == -1 ) {
+ int openErrno = errno;
+ if ( (openErrno == EPERM) && sandboxBlockedOpen(path) )
+ error("file system sandbox blocked open(\"%s\", O_RDONLY)", path);
+ else if ( (openErrno != ENOENT) && (openErrno != ENOTDIR) )
+ error("open(\"%s\", O_RDONLY) failed with errno=%d", path, openErrno);
+ return false;
+ }
+ }
+
+ // Get the realpath of the file if it is a symlink
+ if ( fcntl(fd, F_GETPATH, realerPath) == 0 ) {
+ // Don't set the realpath if it is just the same as the regular path
+ if ( strcmp(originalPath, realerPath) == 0 )
+ realerPath[0] = '\0';
+ } else {
+ error("Could not get real path for \"%s\"\n", path);
+ ::close(fd);
+ return false;
+ }
+
+ // get file info
+ struct stat statBuf;
+#if TARGET_IPHONE_SIMULATOR
+ if ( ::stat(path, &statBuf) != 0 ) {
+#else
+ if ( ::fstat(fd, &statBuf) != 0 ) {
+#endif
+ int statErr = errno;
+ if ( (statErr == EPERM) && sandboxBlockedStat(path) )
+ error("file system sandbox blocked stat(\"%s\")", path);
+ else
+ error("stat(\"%s\") failed with errno=%d", path, errno);
+ ::close(fd);
+ return false;
+ }
+
+ // only regular files can be loaded
+ if ( !S_ISREG(statBuf.st_mode) ) {
+ error("not a file for %s", path);
+ ::close(fd);
+ return false;
+ }
+
+ // mach-o files must be at list one page in size
+ if ( statBuf.st_size < 4096 ) {
+ error("file too short %s", path);
+ ::close(fd);
+ return false;
+ }
+
+ info.fileContent = nullptr;
+ info.fileContentLen = statBuf.st_size;
+ info.sliceOffset = 0;
+ info.sliceLen = statBuf.st_size;
+ info.inode = statBuf.st_ino;
+ info.mtime = statBuf.st_mtime;
+ info.path = originalPath;
+
+ // mmap() whole file
+ void* wholeFile = ::mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE|MAP_RESILIENT_CODESIGN, fd, 0);
+ if ( wholeFile == MAP_FAILED ) {
+ int mmapErr = errno;
+ if ( mmapErr == EPERM ) {
+ if ( sandboxBlockedMmap(path) )
+ error("file system sandbox blocked mmap() of '%s'", path);
+ else
+ error("code signing blocked mmap() of '%s'", path);
+ }
+ else {
+ error("mmap() failed with errno=%d for %s", errno, path);
+ }
+ ::close(fd);
+ return false;
+ }
+ info.fileContent = wholeFile;
+
+ // Set unmap as the unload method.
+ info.unload = [](const LoadedFileInfo& info) {
+ ::munmap((void*)info.fileContent, (size_t)info.fileContentLen);
+ };
+
+ ::close(fd);
+ return true;
+}
+
+void FileSystemPhysical::unloadFile(const LoadedFileInfo& info) const {
+ if (info.unload)
+ info.unload(info);
+}
+
+void FileSystemPhysical::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const {
+ // Unmap from 0..keepStartOffset and (keepStartOffset+keepLength)..info.fileContentLen
+ if (keepStartOffset)
+ ::munmap((void*)info.fileContent, (size_t)keepStartOffset);
+ if ((keepStartOffset + keepLength) != info.fileContentLen) {
+ // Round up to page alignment
+ keepLength = (keepLength + PAGE_SIZE - 1) & (-PAGE_SIZE);
+ ::munmap((void*)((char*)info.fileContent + keepStartOffset + keepLength), (size_t)(info.fileContentLen - (keepStartOffset + keepLength)));
+ }
+ info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
+ info.fileContentLen = keepLength;
+}
+
+bool FileSystemPhysical::fileExists(const char* path, uint64_t* inode, uint64_t* mtime, bool* issetuid) const {
+ struct stat statBuf;
+ if ( _fileSystemPrefix != nullptr ) {
+ char altPath[PATH_MAX];
+ strlcpy(altPath, _fileSystemPrefix, PATH_MAX);
+ strlcat(altPath, path, PATH_MAX);
+ if ( ::stat(altPath, &statBuf) == 0 ) {
+ if (inode)
+ *inode = statBuf.st_ino;
+ if (mtime)
+ *mtime = statBuf.st_mtime;
+ if (issetuid)
+ *issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ return true;
+ }
+ }
+ if ( ::stat(path, &statBuf) != 0 )
+ return false;
+ if (inode)
+ *inode = statBuf.st_ino;
+ if (mtime)
+ *mtime = statBuf.st_mtime;
+ if (issetuid)
+ *issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+ return true;
+}
--- /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 ClosureFileSystemPhysical_h
+#define ClosureFileSystemPhysical_h
+
+#include "ClosureFileSystem.h"
+
+namespace dyld3 {
+namespace closure {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+class __attribute__((visibility("hidden"))) FileSystemPhysical : public FileSystem {
+public:
+ FileSystemPhysical(const char* fileSystemPrefix = nullptr) : FileSystem(), _fileSystemPrefix(fileSystemPrefix) { }
+
+ bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override;
+
+ bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override;
+
+ void unloadFile(const LoadedFileInfo& info) const override;
+
+ void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override;
+
+ bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const override;
+
+private:
+ const char* _fileSystemPrefix;
+};
+#pragma clang diagnostic pop
+
+} // namespace closure
+} // namespace dyld3
+
+#endif /* ClosureFileSystemPhysical_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 "ClosurePrinter.h"
+#include "JSONWriter.h"
+
+using namespace dyld3::json;
+
+namespace dyld3 {
+namespace closure {
+
+static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Image::ResolvedSymbolTarget target)
+{
+ const Image* targetImage;
+ uint64_t value;
+ switch ( target.image.kind ) {
+ case Image::ResolvedSymbolTarget::kindImage:
+ targetImage = ImageArray::findImage(imagesArrays, target.image.imageNum);
+ if ( target.image.offset & 0x8000000000ULL ) {
+ uint64_t signExtend = target.image.offset | 0xFFFFFF0000000000ULL;
+ return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
+ }
+ else
+ return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
+ break;
+ case Image::ResolvedSymbolTarget::kindSharedCache:
+ return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
+ break;
+ case Image::ResolvedSymbolTarget::kindAbsolute:
+ value = target.absolute.value;
+ if ( value & 0x2000000000000000LL )
+ value |= 0xC000000000000000LL;
+ return std::string("bind to absolute ") + hex(value);
+ break;
+ }
+ return "???";
+}
+
+
+static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+{
+ __block Node imageNode;
+
+ if ( image->isInvalid() )
+ return imageNode;
+
+ imageNode.map["image-num"].value = hex4(image->imageNum());
+ imageNode.map["path"].value = image->path();
+ __block Node imageAliases;
+ image->forEachAlias(^(const char* aliasPath, bool& stop) {
+ Node anAlias;
+ anAlias.value = aliasPath;
+ imageAliases.array.push_back(anAlias);
+ });
+ if ( !imageAliases.array.empty() )
+ imageNode.map["aliases"] = imageAliases;
+ uuid_t uuid;
+ if ( image->getUuid(uuid) ) {
+ uuid_string_t uuidStr;
+ uuid_unparse(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["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
+ imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
+// imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
+// if ( image->cwdMustBeThisDir() )
+// imageNode.map["cwd-must-be-this-dir"].value = "true";
+ if ( !image->inDyldCache() ) {
+ 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);
+// }
+ uint64_t inode;
+ uint64_t mTime;
+ if ( image->hasFileModTimeAndInode(inode, mTime) ) {
+ imageNode.map["file-mod-time"].value = hex(inode);
+ imageNode.map["file-inode"].value = hex(mTime);
+ }
+ uint8_t cdHash[20];
+ if ( image->hasCdHash(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" )
+ imageNode.map["cd-hash"].value = cdHashStr;
+ }
+ else {
+ #if 0
+ 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;
+ #endif
+ }
+ 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->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
+ // rebase
+ imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "rebase";
+ }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
+ // bind
+ imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
+ }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
+ // chain
+ imageNode.map["fixups"].map[hex8(imageOffsetStart)].value = "chain-start";
+ for (const Image::ResolvedSymbolTarget& target: targets) {
+ Node targetNode;
+ targetNode.value = printTarget(imagesArrays, target);
+ imageNode.map["fixups-targets"].array.push_back(targetNode);
+ }
+ });
+ image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
+ // rebase
+ imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "text rebase";
+ }, ^(uint32_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
+ imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = "text " + printTarget(imagesArrays, target);
+ });
+ }
+ }
+ else {
+ if ( printFixups ) {
+ image->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* name) {
+ __block Node implNode;
+ implNode.map["name"].value = name;
+ implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
+ image->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(Image::PatchableExport::PatchLocation patchLocation) {
+ Node siteNode;
+ siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
+ if ( patchLocation.addend != 0 )
+ siteNode.map["addend"].value = hex(patchLocation.addend);
+ if ( patchLocation.authenticated != 0 ) {
+ siteNode.map["key"].value = patchLocation.keyName();
+ siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
+ siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
+ }
+ implNode.map["usage-sites"].array.push_back(siteNode);
+ });
+ imageNode.map["patches"].array.push_back(implNode);
+ });
+ }
+ }
+
+ // add dependents
+ image->forEachDependentImage(^(uint32_t depIndex, Image::LinkKind kind, ImageNum imageNum, bool& stop) {
+ Node depMapNode;
+ const Image* depImage = ImageArray::findImage(imagesArrays, imageNum);
+ depMapNode.map["image-num"].value = hex4(imageNum);
+ if ( depImage != nullptr )
+ depMapNode.map["path"].value = depImage->path();
+ 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 initializers
+ image->forEachInitializer(nullptr, ^(const void* initializer) {
+ Node initNode;
+ initNode.value = hex((long)initializer);
+ imageNode.map["initializer-offsets"].array.push_back(initNode);
+ });
+
+ __block Node initBeforeNode;
+ image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
+ Node beforeNode;
+ const Image* initImage = ImageArray::findImage(imagesArrays, imageToInit);
+ assert(initImage != nullptr);
+ beforeNode.value = initImage->path();
+ imageNode.map["initializer-order"].array.push_back(beforeNode);
+ });
+
+ ImageNum cacheImageNum;
+ if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
+ imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
+ }
+
+
+#if 0
+ // 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 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);
+ });
+#endif
+
+ return imageNode;
+}
+
+
+static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+{
+ __block Node images;
+ imageArray->forEachImage(^(const Image* image, bool& stop) {
+ images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, cacheStart));
+ });
+ return images;
+}
+
+
+static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+{
+ __block Node root;
+ root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+
+ closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
+ Node patchNode;
+ patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
+ patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
+ patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
+ root.map["dyld-cache-fixups"].array.push_back(patchNode);
+ });
+
+ return root;
+}
+
+static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+{
+ __block Node root;
+ root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+
+ Image::ResolvedSymbolTarget entry;
+ if ( closure->mainEntry(entry) )
+ root.map["main"].value = printTarget(imagesArrays, entry);
+ else if ( closure->startEntry(entry) )
+ root.map["start"].value = printTarget(imagesArrays, entry);
+
+ Image::ResolvedSymbolTarget libdyldEntry;
+ closure->libDyldEntry(libdyldEntry);
+ root.map["libdyld-entry"].value = printTarget(imagesArrays, libdyldEntry);
+
+ root.map["uses-@paths"].value = (closure->usedAtPaths() ? "true" : "false");
+ root.map["uses-fallback-paths"].value = (closure->usedFallbackPaths() ? "true" : "false");
+
+ // 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);
+ });
+
+ // add interposing info, if any
+ closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
+ Node tupleNode;
+ tupleNode.map["stock"].value = printTarget(imagesArrays, tuple.stockImplementation);
+ tupleNode.map["replace"].value = printTarget(imagesArrays, tuple.newImplementation);
+ root.map["interposing-tuples"].array.push_back(tupleNode);
+ });
+
+ closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
+ Node patchNode;
+ patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
+ patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
+ patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
+ root.map["dyld-cache-fixups"].array.push_back(patchNode);
+ });
+
+ root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
+
+#if 0
+
+
+ // 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 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["group-num"].value = decimal(closure.group().groupNum());
+
+
+ __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;
+#endif
+ return root;
+}
+
+void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+ Node root = buildImageNode(image, imagesArrays, printFixups, false);
+ printJSON(root, 0, out);
+}
+
+void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, FILE* out)
+{
+ const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
+ STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+ imagesArrays.push_back(dylibs);
+
+ Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, (uint8_t*)dyldCache);
+ printJSON(root, 0, out);
+}
+
+void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+ Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+ printJSON(root, 0, out);
+}
+
+void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+ Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+ printJSON(root, 0, out);
+}
+
+
+} // namespace closure
+} // 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 ClosurePrinter_h
+#define ClosurePrinter_h
+
+#include <stdio.h>
+
+#include "Closure.h"
+#include "DyldSharedCache.h"
+
+
+namespace dyld3 {
+namespace closure {
+
+
+void printClosureAsJSON( const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+void printClosureAsJSON( const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+void printImageAsJSON( const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+
+void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups=false, FILE* out=stdout);
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+#endif /* ClosurePrinter_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 <assert.h>
+#include <uuid/uuid.h>
+#include <unistd.h>
+#include <limits.h>
+#include <mach/vm_page_size.h>
+
+#include "ClosureWriter.h"
+#include "MachOFile.h"
+
+
+namespace dyld3 {
+namespace closure {
+
+
+//////////////////////////// ContainerTypedBytesWriter ////////////////////////////////////////
+
+
+void ContainerTypedBytesWriter::setContainerType(TypedBytes::Type containerType)
+{
+ assert(_vmAllocationStart == 0);
+ _vmAllocationSize = 1024 * 1024;
+ vm_address_t allocationAddr;
+ ::vm_allocate(mach_task_self(), &allocationAddr, _vmAllocationSize, VM_FLAGS_ANYWHERE);
+ assert(allocationAddr != 0);
+ _vmAllocationStart = (void*)allocationAddr;
+ _containerTypedBytes = (TypedBytes*)_vmAllocationStart;
+ _containerTypedBytes->type = (uint32_t)containerType;
+ _containerTypedBytes->payloadLength = 0;
+ _end = (uint8_t*)_vmAllocationStart + sizeof(TypedBytes);
+}
+
+void* ContainerTypedBytesWriter::append(TypedBytes::Type t, const void* payload, uint32_t payloadSize)
+{
+ assert((payloadSize & 0x3) == 0);
+ if ( (uint8_t*)_end + payloadSize >= (uint8_t*)_vmAllocationStart + _vmAllocationSize ) {
+ // if current buffer too small, grow it
+ size_t growth = _vmAllocationSize;
+ if ( growth < payloadSize )
+ growth = _vmAllocationSize*((payloadSize/_vmAllocationSize)+1);
+ vm_address_t newAllocationAddr;
+ size_t newAllocationSize = _vmAllocationSize+growth;
+ ::vm_allocate(mach_task_self(), &newAllocationAddr, newAllocationSize, VM_FLAGS_ANYWHERE);
+ assert(newAllocationAddr != 0);
+ size_t currentInUse = (char*)_end - (char*)_vmAllocationStart;
+ memcpy((void*)newAllocationAddr, _vmAllocationStart, currentInUse);
+ ::vm_deallocate(mach_task_self(), (vm_address_t)_vmAllocationStart, _vmAllocationSize);
+ _end = (void*)(newAllocationAddr + currentInUse);
+ _vmAllocationStart = (void*)newAllocationAddr;
+ _containerTypedBytes = (TypedBytes*)_vmAllocationStart;
+ _vmAllocationSize = newAllocationSize;
+ }
+ assert( (uint8_t*)_end + payloadSize < (uint8_t*)_vmAllocationStart + _vmAllocationSize);
+ TypedBytes* tb = (TypedBytes*)_end;
+ tb->type = (uint32_t)t;
+ tb->payloadLength = payloadSize;
+ if ( payload != nullptr )
+ ::memcpy(tb->payload(), payload, payloadSize);
+ _end = (uint8_t*)_end + sizeof(TypedBytes) + payloadSize;
+ _containerTypedBytes->payloadLength += sizeof(TypedBytes) + payloadSize;
+ return tb->payload();
+}
+
+const void* ContainerTypedBytesWriter::finalizeContainer()
+{
+ // trim vm allocation down to just what is needed
+ uintptr_t bufferStart = (uintptr_t)_vmAllocationStart;
+ uintptr_t used = round_page((uintptr_t)_end - bufferStart);
+ if ( used < _vmAllocationSize ) {
+ uintptr_t deallocStart = bufferStart + used;
+ ::vm_deallocate(mach_task_self(), deallocStart, _vmAllocationSize - used);
+ _end = nullptr;
+ _vmAllocationSize = used;
+ }
+ // mark vm region read-only
+ ::vm_protect(mach_task_self(), bufferStart, used, false, VM_PROT_READ);
+ return (void*)_vmAllocationStart;
+}
+
+const void* ContainerTypedBytesWriter::currentTypedBytes()
+{
+ return (void*)_vmAllocationStart;
+}
+
+void ContainerTypedBytesWriter::deallocate()
+{
+ ::vm_deallocate(mach_task_self(), (long)_vmAllocationStart, _vmAllocationSize);
+}
+
+//////////////////////////// ImageWriter ////////////////////////////////////////
+
+
+const Image* ImageWriter::finalize()
+{
+ return (Image*)finalizeContainer();
+}
+
+const Image* ImageWriter::currentImage()
+{
+ return (Image*)currentTypedBytes();
+}
+
+void ImageWriter::addPath(const char* path)
+{
+ uint32_t roundedPathLen = ((uint32_t)strlen(path) + 1 + 3) & (-4);
+ Image::PathAndHash* ph = (Image::PathAndHash*)append(TypedBytes::Type::pathWithHash, nullptr, sizeof(Image::PathAndHash)+roundedPathLen);
+ ph->hash = Image::hashFunction(path);
+ strcpy(ph->path, path);
+}
+
+Image::Flags& ImageWriter::getFlags()
+{
+ if ( _flagsOffset == -1 ) {
+ setContainerType(TypedBytes::Type::image);
+ Image::Flags flags;
+ ::bzero(&flags, sizeof(flags));
+ uint8_t* p = (uint8_t*)append(TypedBytes::Type::imageFlags, &flags, sizeof(flags));
+ _flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
+ }
+ return *((Image::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
+}
+
+void ImageWriter::setImageNum(ImageNum num)
+{
+ getFlags().imageNum = num;
+}
+
+void ImageWriter::setHasObjC(bool value)
+{
+ getFlags().hasObjC = value;
+}
+
+void ImageWriter::setIs64(bool value)
+{
+ getFlags().is64 = value;
+}
+
+void ImageWriter::setHasPlusLoads(bool value)
+{
+ getFlags().mayHavePlusLoads = value;
+}
+
+void ImageWriter::setIsBundle(bool value)
+{
+ getFlags().isBundle = value;
+}
+
+void ImageWriter::setIsDylib(bool value)
+{
+ getFlags().isDylib = value;
+}
+
+void ImageWriter::setIsExecutable(bool value)
+{
+ getFlags().isExecutable = value;
+}
+
+void ImageWriter::setHasWeakDefs(bool value)
+{
+ getFlags().hasWeakDefs = value;
+}
+
+void ImageWriter::setUses16KPages(bool value)
+{
+ getFlags().has16KBpages = value;
+}
+
+void ImageWriter::setOverridableDylib(bool value)
+{
+ getFlags().overridableDylib = value;
+}
+
+void ImageWriter::setInvalid()
+{
+ getFlags().isInvalid = true;
+}
+
+void ImageWriter::setInDyldCache(bool value)
+{
+ getFlags().inDyldCache = value;
+}
+
+void ImageWriter::setNeverUnload(bool value)
+{
+ getFlags().neverUnload = value;
+}
+
+void ImageWriter::setUUID(const uuid_t uuid)
+{
+ append(TypedBytes::Type::uuid, uuid, sizeof(uuid_t));
+}
+
+void ImageWriter::setCDHash(const uint8_t cdHash[20])
+{
+ append(TypedBytes::Type::cdHash, cdHash, 20);
+}
+
+void ImageWriter::setDependents(const Array<Image::LinkedImage>& deps)
+{
+ append(TypedBytes::Type::dependents, deps.begin(), (uint32_t)deps.count()*sizeof(Image::LinkedImage));
+}
+
+void ImageWriter::setDofOffsets(const Array<uint32_t>& dofSectionOffsets)
+{
+ append(TypedBytes::Type::dofOffsets, &dofSectionOffsets[0], (uint32_t)dofSectionOffsets.count()*sizeof(uint32_t));
+}
+
+void ImageWriter::setInitOffsets(const uint32_t initOffsets[], uint32_t count)
+{
+ append(TypedBytes::Type::initOffsets, initOffsets, count*sizeof(uint32_t));
+}
+
+void ImageWriter::setDiskSegments(const Image::DiskSegment segs[], uint32_t count)
+{
+ append(TypedBytes::Type::diskSegment, segs, count*sizeof(Image::DiskSegment));
+}
+
+void ImageWriter::setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count)
+{
+ append(TypedBytes::Type::cacheSegment, segs, count*sizeof(Image::DyldCacheSegment));
+}
+
+void ImageWriter::setCodeSignatureLocation(uint32_t fileOffset, uint32_t size)
+{
+ Image::CodeSignatureLocation loc;
+ loc.fileOffset = fileOffset;
+ loc.fileSize = size;
+ append(TypedBytes::Type::codeSignLoc, &loc, sizeof(loc));
+}
+
+void ImageWriter::setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size)
+{
+ const uint32_t pageSize = getFlags().has16KBpages ? 0x4000 : 0x1000;
+ assert((fileOffset % pageSize) == 0);
+ assert((size % pageSize) == 0);
+ Image::FairPlayRange loc;
+ loc.textStartPage = fileOffset/pageSize;
+ loc.textPageCount = size/pageSize;
+ append(TypedBytes::Type::fairPlayLoc, &loc, sizeof(loc));
+}
+
+void ImageWriter::setMappingInfo(uint64_t sliceOffset, uint64_t vmSize)
+{
+ const uint32_t pageSize = getFlags().has16KBpages ? 0x4000 : 0x1000;
+ Image::MappingInfo info;
+ info.sliceOffsetIn4K = (uint32_t)(sliceOffset / 0x1000);
+ info.totalVmPages = (uint32_t)(vmSize / pageSize);
+ append(TypedBytes::Type::mappingInfo, &info, sizeof(info));
+}
+
+void ImageWriter::setFileInfo(uint64_t inode, uint64_t mTime)
+{
+ Image::FileInfo info = { inode, mTime };
+ append(TypedBytes::Type::fileInodeAndTime, &info, sizeof(info));
+}
+
+void ImageWriter::setRebaseInfo(const Array<Image::RebasePattern>& fixups)
+{
+ append(TypedBytes::Type::rebaseFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::RebasePattern));
+}
+
+void ImageWriter::setTextRebaseInfo(const Array<Image::TextFixupPattern>& fixups)
+{
+ append(TypedBytes::Type::textFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::TextFixupPattern));
+}
+
+void ImageWriter::setBindInfo(const Array<Image::BindPattern>& fixups)
+{
+ append(TypedBytes::Type::bindFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::BindPattern));
+}
+
+void ImageWriter::setChainedFixups(const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets)
+{
+ append(TypedBytes::Type::chainedFixupsStarts, starts.begin(), (uint32_t)starts.count()*sizeof(uint64_t));
+ append(TypedBytes::Type::chainedFixupsTargets, targets.begin(), (uint32_t)targets.count()*sizeof(Image::ResolvedSymbolTarget));
+}
+
+void ImageWriter::addExportPatchInfo(uint32_t implCacheOff, const char* name, uint32_t locCount, const Image::PatchableExport::PatchLocation* locs)
+{
+ uint32_t roundedNameLen = ((uint32_t)strlen(name) + 1 + 3) & (-4);
+ uint32_t payloadSize = sizeof(Image::PatchableExport) + locCount*sizeof(Image::PatchableExport::PatchLocation) + roundedNameLen;
+ Image::PatchableExport* buffer = (Image::PatchableExport*)append(TypedBytes::Type::cachePatchInfo, nullptr, payloadSize);
+ buffer->cacheOffsetOfImpl = implCacheOff;
+ buffer->patchLocationsCount = locCount;
+ memcpy(&buffer->patchLocations[0], locs, locCount*sizeof(Image::PatchableExport::PatchLocation));
+ strcpy((char*)(&buffer->patchLocations[locCount]), name);
+}
+
+void ImageWriter::setAsOverrideOf(ImageNum imageNum)
+{
+ uint32_t temp = imageNum;
+ append(TypedBytes::Type::imageOverride, &temp, sizeof(temp));
+}
+
+void ImageWriter::setInitsOrder(const ImageNum images[], uint32_t count)
+{
+ append(TypedBytes::Type::initBefores, images, count*sizeof(ImageNum));
+}
+
+
+//////////////////////////// ImageArrayWriter ////////////////////////////////////////
+
+
+ImageArrayWriter::ImageArrayWriter(ImageNum startImageNum, unsigned count) : _index(0)
+{
+ setContainerType(TypedBytes::Type::imageArray);
+ _end = (void*)((uint8_t*)_end + sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count);
+ _containerTypedBytes->payloadLength = sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count;
+ ImageArray* ia = (ImageArray*)_containerTypedBytes;
+ ia->firstImageNum = startImageNum;
+ ia->count = count;
+}
+
+void ImageArrayWriter::appendImage(const Image* image)
+{
+ ImageArray* ia = (ImageArray*)_containerTypedBytes;
+ ia->offsets[_index++] = _containerTypedBytes->payloadLength;
+ append(TypedBytes::Type::image, image->payload(), image->payloadLength);
+}
+
+const ImageArray* ImageArrayWriter::finalize()
+{
+ return (ImageArray*)finalizeContainer();
+}
+
+
+//////////////////////////// ClosureWriter ////////////////////////////////////////
+
+void ClosureWriter::setTopImageNum(ImageNum imageNum)
+{
+ append(TypedBytes::Type::topImage, &imageNum, sizeof(ImageNum));
+}
+
+void ClosureWriter::addCachePatches(const Array<Closure::PatchEntry>& patches)
+{
+ append(TypedBytes::Type::cacheOverrides, patches.begin(), (uint32_t)patches.count()*sizeof(Closure::PatchEntry));
+}
+
+
+//////////////////////////// LaunchClosureWriter ////////////////////////////////////////
+
+LaunchClosureWriter::LaunchClosureWriter(const ImageArray* images)
+{
+ setContainerType(TypedBytes::Type::launchClosure);
+ append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
+}
+
+const LaunchClosure* LaunchClosureWriter::finalize()
+{
+ return (LaunchClosure*)finalizeContainer();
+}
+
+void LaunchClosureWriter::setLibSystemImageNum(ImageNum imageNum)
+{
+ append(TypedBytes::Type::libSystemNum, &imageNum, sizeof(ImageNum));
+}
+
+void LaunchClosureWriter::setLibDyldEntry(Image::ResolvedSymbolTarget entry)
+{
+ append(TypedBytes::Type::libDyldEntry, &entry, sizeof(entry));
+}
+
+void LaunchClosureWriter::setMainEntry(Image::ResolvedSymbolTarget main)
+{
+ append(TypedBytes::Type::mainEntry, &main, sizeof(main));
+}
+
+void LaunchClosureWriter::setStartEntry(Image::ResolvedSymbolTarget start)
+{
+ append(TypedBytes::Type::startEntry, &start, sizeof(start));
+}
+
+void LaunchClosureWriter::setUsedFallbackPaths(bool value)
+{
+ getFlags().usedFallbackPaths = value;
+}
+
+void LaunchClosureWriter::setUsedAtPaths(bool value)
+{
+ getFlags().usedAtPaths = value;
+}
+
+void LaunchClosureWriter::setInitImageCount(uint32_t count)
+{
+ getFlags().initImageCount = count;
+}
+
+LaunchClosure::Flags& LaunchClosureWriter::getFlags()
+{
+ if ( _flagsOffset == -1 ) {
+ LaunchClosure::Flags flags;
+ ::bzero(&flags, sizeof(flags));
+ uint8_t* p = (uint8_t*)append(TypedBytes::Type::closureFlags, &flags, sizeof(flags));
+ _flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
+ }
+ return *((LaunchClosure::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
+}
+
+void LaunchClosureWriter::setMustBeMissingFiles(const Array<const char*>& paths)
+{
+ uint32_t totalSize = 0;
+ for (const char* s : paths)
+ totalSize += (strlen(s) +1);
+ totalSize = (totalSize + 3) & (-4); // align
+
+ char* buffer = (char*)append(TypedBytes::Type::missingFiles, nullptr, totalSize);
+ char* t = buffer;
+ for (const char* path : paths) {
+ for (const char* s=path; *s != '\0'; ++s)
+ *t++ = *s;
+ *t++ = '\0';
+ }
+ while (t < &buffer[totalSize])
+ *t++ = '\0';
+}
+
+void LaunchClosureWriter::addEnvVar(const char* envVar)
+{
+ unsigned len = (unsigned)strlen(envVar);
+ char temp[len+8];
+ strcpy(temp, envVar);
+ unsigned paddedSize = len+1;
+ while ( (paddedSize % 4) != 0 )
+ temp[paddedSize++] = '\0';
+ append(TypedBytes::Type::envVar, temp, paddedSize);
+}
+
+void LaunchClosureWriter::addInterposingTuples(const Array<InterposingTuple>& tuples)
+{
+ append(TypedBytes::Type::interposeTuples, tuples.begin(), (uint32_t)tuples.count()*sizeof(InterposingTuple));
+}
+
+void LaunchClosureWriter::setDyldCacheUUID(const uuid_t uuid)
+{
+ append(TypedBytes::Type::dyldCacheUUID, uuid, sizeof(uuid_t));
+}
+
+void LaunchClosureWriter::setBootUUID(const char* uuid)
+{
+ unsigned len = (unsigned)strlen(uuid);
+ char temp[len+8];
+ strcpy(temp, uuid);
+ unsigned paddedSize = len+1;
+ while ( (paddedSize % 4) != 0 )
+ temp[paddedSize++] = '\0';
+ append(TypedBytes::Type::bootUUID, temp, paddedSize);
+}
+
+void LaunchClosureWriter::applyInterposing()
+{
+ const LaunchClosure* currentClosure = (LaunchClosure*)currentTypedBytes();
+ const ImageArray* images = currentClosure->images();
+ currentClosure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool&) {
+ images->forEachImage(^(const dyld3::closure::Image* image, bool&) {
+ for (const Image::BindPattern& bindPat : image->bindFixups()) {
+ if ( (bindPat.target == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
+ Image::BindPattern* writePat = const_cast<Image::BindPattern*>(&bindPat);
+ writePat->target = tuple.newImplementation;
+ }
+ }
+
+ // Chained fixups may also be interposed. We can't change elements in the chain, but we can change
+ // the target list.
+ for (const Image::ResolvedSymbolTarget& symbolTarget : image->chainedTargets()) {
+ if ( (symbolTarget == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
+ Image::ResolvedSymbolTarget* writeTarget = const_cast<Image::ResolvedSymbolTarget*>(&symbolTarget);
+ *writeTarget = tuple.newImplementation;
+ }
+ }
+ });
+ });
+}
+
+//////////////////////////// DlopenClosureWriter ////////////////////////////////////////
+
+DlopenClosureWriter::DlopenClosureWriter(const ImageArray* images)
+{
+ setContainerType(TypedBytes::Type::dlopenClosure);
+ append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
+}
+
+const DlopenClosure* DlopenClosureWriter::finalize()
+{
+ return (DlopenClosure*)finalizeContainer();
+}
+
+
+} // namespace closure
+} // 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 ClosureWriter_h
+#define ClosureWriter_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+#include <uuid/uuid.h>
+
+#include "Closure.h"
+
+
+
+namespace dyld3 {
+
+namespace closure {
+
+
+class VIS_HIDDEN ContainerTypedBytesWriter
+{
+public:
+ void deallocate();
+
+protected:
+ void setContainerType(TypedBytes::Type containerType);
+ void* append(TypedBytes::Type t, const void* payload, uint32_t payloadSize);
+
+ const void* currentTypedBytes();
+ const void* finalizeContainer();
+
+ void* _vmAllocationStart = nullptr;
+ size_t _vmAllocationSize = 0;
+ TypedBytes* _containerTypedBytes = nullptr;
+ void* _end = nullptr;
+};
+
+
+class VIS_HIDDEN ImageWriter : public ContainerTypedBytesWriter
+{
+public:
+
+ void setImageNum(ImageNum num);
+ void addPath(const char* path); // first is canonical, others are aliases
+ void setInvalid();
+ void setInDyldCache(bool);
+ void setIs64(bool);
+ void setHasObjC(bool);
+ void setHasPlusLoads(bool);
+ void setIsBundle(bool);
+ void setIsDylib(bool);
+ void setIsExecutable(bool);
+ void setIsRestricted(bool);
+ void setHasWeakDefs(bool);
+ void setUses16KPages(bool);
+ void setOverridableDylib(bool);
+ void setNeverUnload(bool);
+ void setUUID(const uuid_t uuid);
+ void setCDHash(const uint8_t cdHash[20]);
+ void setDependents(const Array<Image::LinkedImage>& deps);
+ void setDofOffsets(const Array<uint32_t>& dofSectionOffsets);
+ void setInitOffsets(const uint32_t initOffsets[], uint32_t count);
+ void setDiskSegments(const Image::DiskSegment segs[], uint32_t count);
+ void setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count);
+ void setCodeSignatureLocation(uint32_t fileOffset, uint32_t size);
+ void setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size);
+ void setMappingInfo(uint64_t sliceOffset, uint64_t vmSize);
+ void setFileInfo(uint64_t inode, uint64_t modTime);
+ void setRebaseInfo(const Array<Image::RebasePattern>&);
+ void setTextRebaseInfo(const Array<Image::TextFixupPattern>&);
+ void setBindInfo(const Array<Image::BindPattern>&);
+ void setAsOverrideOf(ImageNum);
+ void addExportPatchInfo(uint32_t implOff, const char* name, uint32_t locCount, const Image::PatchableExport::PatchLocation* locs);
+ void setInitsOrder(const ImageNum images[], uint32_t count);
+ void setChainedFixups(const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets);
+
+ const Image* currentImage();
+
+ const Image* finalize();
+
+private:
+ Image::Flags& getFlags();
+
+ int _flagsOffset = -1;
+};
+
+
+class VIS_HIDDEN ImageArrayWriter : public ContainerTypedBytesWriter
+{
+public:
+ ImageArrayWriter(ImageNum startImageNum, unsigned count);
+
+ void appendImage(const Image*);
+ const ImageArray* finalize();
+private:
+ unsigned _index;
+};
+
+class VIS_HIDDEN ClosureWriter : public ContainerTypedBytesWriter
+{
+public:
+ void setTopImageNum(ImageNum imageNum);
+ void addCachePatches(const Array<Closure::PatchEntry>&);
+};
+
+class VIS_HIDDEN LaunchClosureWriter : public ClosureWriter
+{
+public:
+ LaunchClosureWriter(const ImageArray* images);
+
+ const LaunchClosure* finalize();
+ void setLibSystemImageNum(ImageNum imageNum);
+ void setInitImageCount(uint32_t count);
+ void setLibDyldEntry(Image::ResolvedSymbolTarget dyldEntry);
+ void setMainEntry(Image::ResolvedSymbolTarget main);
+ void setStartEntry(Image::ResolvedSymbolTarget start);
+ void setUsedFallbackPaths(bool);
+ void setUsedAtPaths(bool);
+ void setMustBeMissingFiles(const Array<const char*>& paths);
+ void addInterposingTuples(const Array<InterposingTuple>& tuples);
+ void setDyldCacheUUID(const uuid_t);
+ void setBootUUID(const char* uuid);
+ void applyInterposing();
+ void addEnvVar(const char* envVar);
+
+private:
+ LaunchClosure::Flags& getFlags();
+
+ int _flagsOffset = -1;
+};
+
+
+class VIS_HIDDEN DlopenClosureWriter : public ClosureWriter
+{
+public:
+ DlopenClosureWriter(const ImageArray* images);
+
+ const DlopenClosure* finalize();
+
+};
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+#endif // ClosureWriter_h
+
CS_HASHTYPE_SHA1 = 1,
CS_HASHTYPE_SHA256 = 2,
CS_HASHTYPE_SHA256_TRUNCATED = 3,
+ CS_HASHTYPE_SHA384 = 4,
CS_HASH_SIZE_SHA1 = 20,
CS_HASH_SIZE_SHA256 = 32,
#endif
Diagnostics::Diagnostics(bool verbose)
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
: _verbose(verbose)
, _prefix("")
#endif
{
}
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
Diagnostics::Diagnostics(const std::string& prefix, bool verbose)
: _verbose(verbose)
, _prefix(prefix)
void Diagnostics::error(const char* format, ...)
{
- _buffer = _simple_salloc();
va_list list;
va_start(list, format);
- _simple_vsprintf(_buffer, format, list);
+ error(format, list);
va_end(list);
+}
+
+void Diagnostics::error(const char* format, va_list list)
+{
+ _buffer = _simple_salloc();
+ _simple_vsprintf(_buffer, format, list);
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
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);
abort_report_np("%s", _simple_string(_buffer));
}
-#if DYLD_IN_PROCESS
+#if !BUILDING_CACHE_BUILDER
const char* Diagnostics::errorMessage() const
{
return _simple_string(_buffer);
#include <stdint.h>
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
#include <set>
#include <string>
#include <vector>
~Diagnostics();
void error(const char* format, ...) __attribute__((format(printf, 2, 3)));
-#if !DYLD_IN_PROCESS
+ void error(const char* format, va_list list);
+#if BUILDING_CACHE_BUILDER
Diagnostics(const std::string& prefix, bool verbose=false);
void warning(const char* format, ...) __attribute__((format(printf, 2, 3)));
void clearError();
void assertNoError() const;
-#if DYLD_IN_PROCESS
+#if !BUILDING_CACHE_BUILDER
const char* errorMessage() const;
#else
const std::string prefix() const;
private:
void* _buffer = nullptr;
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
std::string _prefix;
std::set<std::string> _warnings;
bool _verbose = false;
+++ /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@
+ */
+
+#include <string.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+namespace dyld3 {
+namespace json {
+
+struct Node
+{
+ std::string value;
+ std::map<std::string, Node> map;
+ std::vector<Node> array;
+};
+
+static inline std::string hex(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%llX", value);
+ return buff;
+}
+
+static inline std::string hex4(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%04llX", value);
+ return buff;
+}
+
+static inline std::string hex8(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "0x%08llX", value);
+ return buff;
+}
+
+static inline std::string decimal(uint64_t value) {
+ char buff[64];
+ sprintf(buff, "%llu", value);
+ return buff;
+}
+
+static inline void indentBy(uint32_t spaces, FILE* out) {
+ for (int i=0; i < spaces; ++i) {
+ fprintf(out, " ");
+ }
+}
+
+static inline 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");
+}
+
+
+} // namespace json
+} // 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 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);
- }) ) {
- switch (foundInfo.kind) {
- case MachOParser::FoundSymbol::Kind::headerOffset:
- case MachOParser::FoundSymbol::Kind::resolverOffset:
- result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
- break;
- case MachOParser::FoundSymbol::Kind::absolute:
- result = (uintptr_t)foundInfo.value;
- break;
- }
- 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) ) {
- switch (foundInfo.kind) {
- case MachOParser::FoundSymbol::Kind::headerOffset:
- case MachOParser::FoundSymbol::Kind::resolverOffset:
- result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
- break;
- case MachOParser::FoundSymbol::Kind::absolute:
- result = (uintptr_t)foundInfo.value;
- break;
- }
- 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) ) {
- switch (foundInfo.kind) {
- case MachOParser::FoundSymbol::Kind::headerOffset:
- case MachOParser::FoundSymbol::Kind::resolverOffset:
- result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
- break;
- case MachOParser::FoundSymbol::Kind::absolute:
- result = (uintptr_t)foundInfo.value;
- break;
- }
- 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) ) {
- switch (foundInfo.kind) {
- case MachOParser::FoundSymbol::Kind::headerOffset:
- case MachOParser::FoundSymbol::Kind::resolverOffset:
- result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
- break;
- case MachOParser::FoundSymbol::Kind::absolute:
- result = (uintptr_t)foundInfo.value;
- break;
- }
- 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
-
-
*/
+#include <bitset>
#include <stdint.h>
#include <string.h>
#include <sandbox.h>
#include <sandbox/private.h>
#include <dispatch/dispatch.h>
+#include <mach/vm_page_size.h>
-#include "LaunchCache.h"
-#include "LaunchCacheFormat.h"
+#include "MachOFile.h"
+#include "MachOLoaded.h"
#include "Logging.h"
#include "Loading.h"
-#include "MachOParser.h"
+#include "Tracing.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
+namespace {
-static bool sandboxBlocked(const char* path, const char* kind)
+// utility to track a set of ImageNum's in use
+class VIS_HIDDEN ImageNumSet
{
-#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
+public:
+ void add(dyld3::closure::ImageNum num);
+ bool contains(dyld3::closure::ImageNum num) const;
+
+private:
+ std::bitset<5120> _bitmap;
+ dyld3::OverflowSafeArray<dyld3::closure::ImageNum> _overflowArray;
+};
+
+void ImageNumSet::add(dyld3::closure::ImageNum num)
+{
+ if ( num < 5120 )
+ _bitmap.set(num);
+ else
+ _overflowArray.push_back(num);
+}
+
+bool ImageNumSet::contains(dyld3::closure::ImageNum num) const
+{
+ if ( num < 5120 )
+ return _bitmap.test(num);
+
+ for (dyld3::closure::ImageNum existingNum : _overflowArray) {
+ if ( existingNum == num )
+ return true;
+ }
return false;
-#endif
}
+} // namespace anonymous
+
-static bool sandboxBlockedMmap(const char* path)
+namespace dyld3 {
+
+Loader::Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
+ LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
+ : _allImages(storage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress),
+ _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
{
- return sandboxBlocked(path, "file-map-executable");
}
-static bool sandboxBlockedOpen(const char* path)
+void Loader::addImage(const LoadedImage& li)
{
- return sandboxBlocked(path, "file-read-data");
+ _allImages.push_back(li);
}
-static bool sandboxBlockedStat(const char* path)
+LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
{
- return sandboxBlocked(path, "file-read-metadata");
+ for (LoadedImage& info : _allImages) {
+ if ( info.image()->representsImageNum(targetImageNum) )
+ return &info;
+ }
+ return nullptr;
}
-#if TARGET_OS_WATCH || TARGET_OS_BRIDGE
-static uint64_t pageAlign(uint64_t value)
+uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
{
- #if __arm64__
- return (value + 0x3FFF) & (-0x4000);
- #else
- return (value + 0xFFF) & (-0x1000);
- #endif
+ const LoadedImage* info;
+ switch ( target.sharedCache.kind ) {
+ case closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ assert(_dyldCacheAddress != nullptr);
+ return (uintptr_t)_dyldCacheAddress + (uintptr_t)target.sharedCache.offset;
+
+ case closure::Image::ResolvedSymbolTarget::kindImage:
+ info = findImage(target.image.imageNum);
+ assert(info != nullptr);
+ return (uintptr_t)(info->loadedAddress()) + (uintptr_t)target.image.offset;
+
+ case closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ if ( target.absolute.value & (1ULL << 62) )
+ return (uintptr_t)(target.absolute.value | 0xC000000000000000ULL);
+ else
+ return (uintptr_t)target.absolute.value;
+ }
+ assert(0 && "malformed ResolvedSymbolTarget");
+ return 0;
}
-#endif
-static void updateSliceOffset(uint64_t& sliceOffset, uint64_t codeSignEndOffset, size_t fileLen)
+
+void Loader::completeAllDependents(Diagnostics& diag, uintptr_t topIndex)
{
-#if TARGET_OS_WATCH || TARGET_OS_BRIDGE
- if ( sliceOffset != 0 ) {
- if ( pageAlign(codeSignEndOffset) == pageAlign(fileLen) ) {
- // cache builder saw fat file, but file is now thin
- sliceOffset = 0;
- return;
+ // accumulate all image overrides
+ STACK_ALLOC_ARRAY(ImageOverride, overrides, _allImages.maxCount());
+ for (const auto anArray : _imagesArrays) {
+ // ignore prebuilt Image* in dyld cache
+ if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
+ continue;
+ anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ ImageOverride overrideEntry;
+ if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) {
+ overrideEntry.replacement = image->imageNum();
+ overrides.push_back(overrideEntry);
+ }
+ });
+ }
+
+ // make cache for fast lookup of already loaded images
+ __block ImageNumSet alreadyLoaded;
+ for (int i=0; i <= topIndex; ++i) {
+ alreadyLoaded.add(_allImages[i].image()->imageNum());
+ }
+
+ // for each image in _allImages, starting at topIndex, make sure its depenents are in _allImages
+ uintptr_t index = topIndex;
+ while ( (index < _allImages.count()) && diag.noError() ) {
+ const closure::Image* image = _allImages[index].image();
+ //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path());
+ image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
+ // check if imageNum needs to be changed to an override
+ for (const ImageOverride& entry : overrides) {
+ if ( entry.inCache == depImageNum ) {
+ depImageNum = entry.replacement;
+ break;
+ }
+ }
+ // check if this dependent is already loaded
+ if ( !alreadyLoaded.contains(depImageNum) ) {
+ // if not, look in imagesArrays
+ const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum);
+ if ( depImage != nullptr ) {
+ //dyld::log(" load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path());
+ if ( _allImages.freeCount() == 0 ) {
+ diag.error("too many initial images");
+ stop = true;
+ }
+ else {
+ _allImages.push_back(LoadedImage::make(depImage));
+ }
+ alreadyLoaded.add(depImageNum);
+ }
+ else {
+ diag.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum, depIndex, image->path());
+ stop = true;
+ }
+ }
+ });
+ ++index;
+ }
+}
+
+void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, uintptr_t topIndex)
+{
+ // scan array and map images not already loaded
+ for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+ LoadedImage& info = _allImages[i];
+ if ( info.loadedAddress() != nullptr ) {
+ // log main executable's segments
+ if ( (info.loadedAddress()->filetype == MH_EXECUTE) && (info.state() == LoadedImage::State::mapped) ) {
+ if ( _logSegments("dyld: mapped by kernel %s\n", info.image()->path()) ) {
+ info.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.loadedAddress() + vmOffset;
+ uint64_t end = start+vmSize-1;
+ if ( (segIndex == 0) && (permissions == 0) ) {
+ start = 0;
+ }
+ _logSegments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", info.loadedAddress()->segmentName(segIndex),
+ (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+ start, end);
+ });
+ }
+ }
+ // skip over ones already loaded
+ continue;
+ }
+ if ( info.image()->inDyldCache() ) {
+ if ( info.image()->overridableDylib() ) {
+ struct stat statBuf;
+ if ( stat(info.image()->path(), &statBuf) == 0 ) {
+ // verify file has not changed since closure was built
+ uint64_t inode;
+ uint64_t mtime;
+ if ( info.image()->hasFileModTimeAndInode(inode, mtime) ) {
+ if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
+ diag.error("dylib file mtime/inode changed since closure was built for '%s'", info.image()->path());
+ }
+ }
+ else {
+ diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
+ }
+ }
+ else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
+ diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
+ }
+ }
+ if ( diag.noError() ) {
+ info.setLoadedAddress((MachOLoaded*)((uintptr_t)_dyldCacheAddress + info.image()->cacheOffset()));
+ info.setState(LoadedImage::State::fixedUp);
+ if ( _logSegments("dyld: Using from dyld cache %s\n", info.image()->path()) ) {
+ info.image()->forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+ _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", info.loadedAddress()->segmentName(segIndex),
+ (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+ (long)info.loadedAddress()+(long)vmOffset, (long)info.loadedAddress()+(long)vmOffset+(long)vmSize-1);
+ });
+ }
+ }
+ }
+ else {
+ mapImage(diag, info, fromOFI);
+ if ( diag.hasError() )
+ break; // out of for loop
+ }
+
+ }
+ if ( diag.hasError() ) {
+ // bummer, need to clean up by unmapping any images just mapped
+ for (LoadedImage& info : _allImages) {
+ if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
+ _logSegments("dyld: unmapping %s\n", info.image()->path());
+ unmapImage(info);
+ }
+ }
+ return;
+ }
+
+ // apply fixups
+ for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+ LoadedImage& info = _allImages[i];
+ // images in shared cache do not need fixups applied
+ if ( info.image()->inDyldCache() )
+ continue;
+ // previously loaded images were previously fixed up
+ if ( info.state() < LoadedImage::State::fixedUp ) {
+ applyFixupsToImage(diag, info);
+ if ( diag.hasError() )
+ break;
+ info.setState(LoadedImage::State::fixedUp);
}
}
+
+ // find and register dtrace DOFs
+ if ( processDOFs ) {
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _allImages.count());
+ for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+ LoadedImage& info = _allImages[i];
+ info.image()->forEachDOF(info.loadedAddress(), ^(const void* section) {
+ DOFInfo dofInfo;
+ dofInfo.dof = section;
+ dofInfo.imageHeader = info.loadedAddress();
+ dofInfo.imageShortName = info.image()->leafName();
+ dofImages.push_back(dofInfo);
+ });
+ }
+ registerDOFs(dofImages);
+ }
+}
+
+bool Loader::sandboxBlocked(const char* path, const char* kind)
+{
+#if TARGET_IPHONE_SIMULATOR
+ // sandbox calls not yet supported in dyld_sim
+ return false;
+#else
+ sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+ return ( sandbox_check(getpid(), kind, filter, path) > 0 );
#endif
}
-static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagnostics& diag, LogFunc log_loads, LogFunc log_segments)
+bool Loader::sandboxBlockedMmap(const char* path)
{
- 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;
+ return sandboxBlocked(path, "file-map-executable");
+}
+
+bool Loader::sandboxBlockedOpen(const char* path)
+{
+ return sandboxBlocked(path, "file-read-data");
+}
+
+bool Loader::sandboxBlockedStat(const char* path)
+{
+ return sandboxBlocked(path, "file-read-metadata");
+}
+
+void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
+{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
+
+ const closure::Image* image = info.image();
+ uint64_t sliceOffset = image->sliceOffsetInFile();
+ const uint64_t totalVMSize = image->vmSizeToMap();
+ uint32_t codeSignFileOffset;
+ uint32_t codeSignFileSize;
+ bool isCodeSigned = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
// open file
- int fd = ::open(image.path(), O_RDONLY, 0);
+ int fd = ::open(info.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());
+ 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;
+ diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image->path(), openErr);
+ return;
}
// get file info
struct stat statBuf;
#if TARGET_IPHONE_SIMULATOR
- if ( stat(image.path(), &statBuf) != 0 ) {
+ 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());
+ 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);
+ diag.error("stat(\"%s\") failed with errno=%d", image->path(), statErr);
close(fd);
- return nullptr;
+ return;
}
// 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());
+ uint64_t inode;
+ uint64_t mtime;
+ if ( image->hasFileModTimeAndInode(inode, mtime) ) {
+ if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
+ diag.error("file mtime/inode changed since closure was built for '%s'", image->path());
close(fd);
- return nullptr;
+ return;
}
}
- // handle OS dylibs being thinned after closure was built
- if ( image.group().groupNum() == 1 )
- updateSliceOffset(sliceOffset, codeSignFileOffset+codeSignFileSize, (size_t)statBuf.st_size);
+ // handle case on iOS where sliceOffset in closure is wrong because file was thinned after cache was built
+ if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) {
+ if ( sliceOffset != 0 ) {
+ if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) {
+ // file is now thin
+ sliceOffset = 0;
+ }
+ }
+ }
// register code signature
uint64_t coveredCodeLength = UINT64_MAX;
- if ( codeSignFileOffset != 0 ) {
+ if ( isCodeSigned ) {
+ auto sigTimer = ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 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
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());
+ 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());
+ errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
}
close(fd);
- return nullptr;
+ return;
}
coveredCodeLength = siginfo.fs_file_start;
- if ( coveredCodeLength < image.asDiskImage()->codeSignFileOffset ) {
+ if ( coveredCodeLength < codeSignFileOffset ) {
diag.error("code signature does not cover entire file up to signature");
close(fd);
- return nullptr;
+ return;
}
+ }
+ // <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries
+ {
// <rdar://problem/32684903> always call F_CHECK_LV to preflight
fchecklv checkInfo;
char messageBuffer[512];
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);
+ diag.error("code signature in (%s) not valid for use in process: %s", image->path(), messageBuffer);
close(fd);
- return nullptr;
+ return;
}
}
if ( r != KERN_SUCCESS ) {
diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
close(fd);
- return nullptr;
+ return;
}
if ( sliceOffset != 0 )
- log_segments("dyld: Mapping %s (slice offset=%llu)\n", image.path(), sliceOffset);
+ _logSegments("dyld: Mapping %s (slice offset=%llu)\n", image->path(), sliceOffset);
else
- log_segments("dyld: Mapping %s\n", image.path());
+ _logSegments("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) {
+ 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;
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());
+ 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());
+ 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());
+ 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;
// sanity check first segment is mach-o header
if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
mappedFirstSegment = true;
- if ( !MachOParser::isMachO(diag, segAddress, fileSize) ) {
+ const MachOFile* mf = (MachOFile*)segAddress;
+ if ( !mf->isMachO(diag, 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),
+ const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
+ _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", lmo->segmentName(segIndex),
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
- (long)segAddress, (long)segAddress+vmSize-1);
+ (long)segAddress, (long)segAddress+(long)vmSize-1);
}
});
if ( mmapFailure ) {
- vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
- close(fd);
- return nullptr;
+ ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ ::close(fd);
+ return;
}
// close file
close(fd);
- #if BUILDING_LIBDYLD
+#if BUILDING_LIBDYLD
// verify file has not changed since closure was built by checking code signature has not changed
- if ( image.validateUsingCdHash() ) {
+ uint8_t cdHashExpected[20];
+ if ( image->hasCdHash(cdHashExpected) ) {
if ( codeSignatureStartAddress == nullptr ) {
diag.error("code signature missing");
}
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 )
+ uint8_t cdHashFound[20];
+ const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
+ if ( lmo->cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHashFound) ) {
+ if ( ::memcmp(cdHashFound, cdHashExpected, 20) != 0 )
diag.error("code signature changed since closure was built");
}
- else{
+ else {
diag.error("code signature format invalid");
}
}
if ( diag.hasError() ) {
- vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
- return nullptr;
+ ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ return;
}
}
#endif
// tell kernel about fairplay encrypted regions
uint32_t fpTextOffset;
uint32_t fpSize;
- if ( image.isFairPlayEncrypted(fpTextOffset, 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;
+ int result = ::mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
+ if ( result != 0 ) {
+ diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
+ ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+ return;
+ }
}
#endif
- log_loads("dyld: load %s\n", image.path());
+ _logLoads("dyld: load %s\n", image->path());
- return (mach_header*)loadAddress;
+ timer.setData4((uint64_t)loadAddress);
+ info.setLoadedAddress((MachOLoaded*)loadAddress);
+ info.setState(LoadedImage::State::mapped);
}
-
-void unmapImage(const launch_cache::binary_format::Image* binImage, const mach_header* loadAddress)
+void Loader::unmapImage(LoadedImage& info)
{
- assert(loadAddress != nullptr);
- launch_cache::Image image(binImage);
- vm_deallocate(mach_task_self(), (vm_address_t)loadAddress, (vm_size_t)(image.vmSizeToMap()));
+ assert(info.loadedAddress() != nullptr);
+ ::vm_deallocate(mach_task_self(), (vm_address_t)info.loadedAddress(), (vm_size_t)(info.image()->vmSizeToMap()));
+ info.setLoadedAddress(nullptr);
}
-
-static void applyFixupsToImage(Diagnostics& diag, const mach_header* imageMH, const launch_cache::binary_format::Image* imageData,
- launch_cache::TargetSymbolValue::LoadedImages& imageResolver, LogFunc log_fixups)
+void Loader::registerDOFs(const Array<DOFInfo>& dofs)
{
- 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;
+ if ( dofs.empty() )
+ return;
+
+ int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+ if ( fd < 0 ) {
+ _logDofs("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) + dofs.count()*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 = dofs.count();
+ for (unsigned int i=0; i < dofs.count(); ++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);
}
- #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;
+
+ // 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 < dofs.count(); ++i) {
+ _logDofs("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));
}
}
- #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;
+ else {
+ _logDofs("dyld: ioctl to register dtrace DOF section failed\n");
}
- });
- return result;
+ close(fd);
+ }
}
-void CurrentLoadImages::forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop))
+bool Loader::dtraceUserProbesEnabled()
{
- 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;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+ int dof_mode;
+ size_t dof_mode_size = sizeof(dof_mode);
+ if ( sysctlbyname("kern.dtrace.dof_mode", &dof_mode, &dof_mode_size, nullptr, 0) == 0 ) {
+ return ( dof_mode != 0 );
}
+ return false;
+#else
+ // dtrace is always available for macOS and simulators
+ return true;
+#endif
}
-struct DOFInfo {
- const void* dof;
- const mach_header* imageHeader;
- const char* imageShortName;
-};
-static void registerDOFs(const DOFInfo* dofs, uint32_t dofSectionCount, LogFunc log_dofs)
+void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
{
- 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);
- }
- }
+#if __arm__ || __arm64__
+ // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
+ logger("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 mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
- LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs)
+void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
{
- // 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);
- });
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
+ closure::ImageNum cacheImageNum;
+ const char* leafName = info.image()->leafName();
+ const closure::Image* image = info.image();
+ const uint8_t* imageLoadAddress = (uint8_t*)info.loadedAddress();
+ uintptr_t slide = info.loadedAddress()->getSlide();
+ bool overrideOfCache = info.image()->isOverrideOfDyldCacheImage(cacheImageNum);
+ if ( overrideOfCache )
+ vmAccountingSetSuspended(true, _logFixups);
+ image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
+ uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
+ *fixUpLoc += slide;
+ _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+ },
+ ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool &stop) {
+ uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
+ uintptr_t value = resolveTarget(bindTarget);
+ _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+ *fixUpLoc = value;
+ },
+ ^(uint64_t imageOffsetStart, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
+ // walk each fixup in the chain
+ image->forEachChainedFixup((void*)imageLoadAddress, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
+ if ( fixupInfo.authRebase.auth ) {
+ #if __has_feature(ptrauth_calls)
+ if ( fixupInfo.authBind.bind ) {
+ closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
+ uint64_t targetAddr = resolveTarget(bindTarget);
+ // Don't sign missing weak imports.
+ if (targetAddr != 0)
+ targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
+ _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+ fixupLoc, (void*)targetAddr, fixupInfo.authBind.diversity, fixupInfo.authBind.addrDiv, fixupInfo.authBind.keyName());
+ *fixupLoc = targetAddr;
}
+ else {
+ uint64_t targetAddr = (uint64_t)imageLoadAddress + fixupInfo.authRebase.target;
+ targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
+ _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+ fixupLoc, (void*)targetAddr, fixupInfo.authRebase.diversity, fixupInfo.authRebase.addrDiv, fixupInfo.authRebase.keyName());
+ *fixupLoc = targetAddr;
+ }
+ #else
+ diag.error("malformed chained pointer");
+ stop = true;
+ stopChain = true;
+ #endif
}
- // 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 ( fixupInfo.plainRebase.bind ) {
+ closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
+ uint64_t targetAddr = resolveTarget(bindTarget) + fixupInfo.plainBind.signExtendedAddend();
+ _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixupLoc, (void*)targetAddr);
+ *fixupLoc = targetAddr;
}
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
- }
+ uint64_t targetAddr = fixupInfo.plainRebase.signExtendedTarget() + slide;
+ _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixupLoc, (void*)slide);
+ *fixupLoc = targetAddr;
}
}
- 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;
- }
+#if __i386__
+ __block bool segmentsMadeWritable = false;
+ image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool& stop) {
+ if ( !segmentsMadeWritable )
+ setSegmentProtects(info, true);
+ uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
+ *fixUpLoc += slide;
+ _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+ },
+ ^(uint32_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
+ // FIXME
+ });
+ if ( segmentsMadeWritable )
+ setSegmentProtects(info, false);
+#endif
- // 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;
- });
- }
+ if ( overrideOfCache )
+ vmAccountingSetSuspended(false, _logFixups);
+}
- // 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;
- });
+#if __i386__
+void Loader::setSegmentProtects(const LoadedImage& info, bool write)
+{
+ info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
+ if ( protections & VM_PROT_WRITE )
+ return;
+ uint32_t regionProt = protections;
+ if ( write )
+ regionProt = VM_PROT_WRITE | VM_PROT_READ;
+ kern_return_t r = vm_protect(mach_task_self(), ((uintptr_t)info.loadedAddress())+(uintptr_t)vmOffset, (uintptr_t)vmSize, false, regionProt);
+ assert( r == KERN_SUCCESS );
+ });
+}
+#endif
+
+#if BUILDING_DYLD
+void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
+{
+ bool stop = false;
+ const char* const eof = &buffer[bufferLen];
+ for (const char* s = buffer; s < eof; ++s) {
+ char lineBuffer[MAXPATHLEN];
+ char* t = lineBuffer;
+ char* tEnd = &lineBuffer[MAXPATHLEN];
+ while ( (s < eof) && (t != tEnd) ) {
+ if ( *s == '\n' )
+ break;
+ *t++ = *s++;
}
- registerDOFs(dofImages, dofCount, log_dofs);
+ *t = '\0';
+ lineHandler(lineBuffer, stop);
+ if ( stop )
+ break;
}
}
-#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 ( 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;
- }
+ forEachLineInFile(lines, (size_t)statBuf.st_size, lineHandler);
munmap((void*)lines, (size_t)statBuf.st_size);
}
}
}
#endif
-
-#endif // DYLD_IN_PROCESS
-
-} // namespace loader
} // namespace dyld3
#include <stdint.h>
#include <mach/mach.h>
#include <_simple.h>
-#include "LaunchCache.h"
-#include "LaunchCacheFormat.h"
-#include "MachOParser.h"
-#include "ClosureBuffer.h"
-
+#include "Closure.h"
+#include "MachOLoaded.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;
+//
+// Tuple of info about a loaded image. Contains the loaded address, Image*, and state.
+//
+class VIS_HIDDEN LoadedImage {
+public:
+ enum class State { unmapped=0, mapped=1, fixedUp=2, beingInited=3, inited=4 };
+
+ static LoadedImage make(const closure::Image* img) { LoadedImage result; result._image = img; return result; }
+ static LoadedImage make(const closure::Image* img, const MachOLoaded* mh)
+ { LoadedImage result; result._image = img; result.setLoadedAddress(mh); return result; }
+
+ const closure::Image* image() const { return _image; }
+ const MachOLoaded* loadedAddress() const { return (MachOLoaded*)(_loadAddr & (-4096)); }
+ void setLoadedAddress(const MachOLoaded* a) { _loadAddr |= ((uintptr_t)a & (-4096)); }
+ State state() const { return (State)(asBits().state); }
+ void setState(State s) { asBits().state = (int)s; }
+ bool hideFromFlatSearch() const { return asBits().hide; }
+ void setHideFromFlatSearch(bool h) { asBits().hide = h; }
+ bool leaveMapped() const { return asBits().leaveMapped; }
+ void markLeaveMapped() { asBits().leaveMapped = true; }
+
+private:
+ // since loaded MachO files are always page aligned, that means at least low 12-bits are always zero
+ // so we don't need to record the low 12 bits, instead those bits hold various flags in the _loadeAddr field
+ struct AddrBits {
+ uintptr_t state : 3,
+ hide : 1,
+ leaveMapped : 1,
+ extra : 7,
+ #if __LP64__
+ addr : 52;
+ #else
+ addr : 20;
+ #endif
+ };
+ AddrBits& asBits() { return *((AddrBits*)&_loadAddr); }
+ const AddrBits& asBits() const { return *((AddrBits*)&_loadAddr); }
+
+ // Note: this must be statically initializable so as to not cause static initializers
+ const closure::Image* _image = nullptr;
+ uintptr_t _loadAddr = 0; // really AddrBits
};
-#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;
+//
+// Utility class to recursively load dependents
+//
+class VIS_HIDDEN Loader {
+public:
+ typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
+
+ Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
+ LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs);
+
+ void addImage(const LoadedImage&);
+ void completeAllDependents(Diagnostics& diag, uintptr_t topIndex=0);
+ void mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI=false, uintptr_t topIndex=0);
+ uintptr_t resolveTarget(closure::Image::ResolvedSymbolTarget target);
+ LoadedImage* findImage(closure::ImageNum targetImageNum);
+
+ static void unmapImage(LoadedImage& info);
+ static bool dtraceUserProbesEnabled();
+ static void vmAccountingSetSuspended(bool suspend, LogFunc);
+
+private:
+
+ struct ImageOverride
+ {
+ closure::ImageNum inCache;
+ closure::ImageNum replacement;
+ };
+
+ struct DOFInfo {
+ const void* dof;
+ const mach_header* imageHeader;
+ const char* imageShortName;
+ };
+
+ void mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI);
+ void applyFixupsToImage(Diagnostics& diag, LoadedImage& info);
+ void registerDOFs(const Array<DOFInfo>& dofs);
+ void setSegmentProtects(const LoadedImage& info, bool write);
+ bool sandboxBlockedMmap(const char* path);
+ bool sandboxBlockedOpen(const char* path);
+ bool sandboxBlockedStat(const char* path);
+ bool sandboxBlocked(const char* path, const char* kind);
+
+ Array<LoadedImage>& _allImages;
+ const Array<const closure::ImageArray*>& _imagesArrays;
+ const void* _dyldCacheAddress;
+ LogFunc _logLoads;
+ LogFunc _logSegments;
+ LogFunc _logFixups;
+ LogFunc _logDofs;
+};
-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));
+void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop));
#endif
-#endif
-} // 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@
+ */
+
+#include <sys/types.h>
+#include <mach/mach.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach-o/reloc.h>
+#include <mach-o/nlist.h>
+#include <TargetConditionals.h>
+
+#include "MachOAnalyzer.h"
+#include "CodeSigningTypes.h"
+#include "Array.h"
+
+#include <stdio.h>
+
+
+#ifndef BIND_OPCODE_THREADED
+ #define BIND_OPCODE_THREADED 0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+ #define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+ #define BIND_SUBOPCODE_THREADED_APPLY 0x01
+#endif
+
+
+namespace dyld3 {
+
+
+const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength, const char* reqArchName, Platform reqPlatform)
+{
+ const MachOAnalyzer* result = (const MachOAnalyzer*)mh;
+ if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, reqArchName, reqPlatform) )
+ return nullptr;
+ if ( !result->isDynamicExecutable() )
+ return nullptr;
+
+ return result;
+}
+
+
+closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* path, const char* reqArchName, Platform reqPlatform)
+{
+ closure::LoadedFileInfo info;
+ char realerPath[MAXPATHLEN];
+ if (!fileSystem.loadFile(path, info, realerPath, ^(const char *format, ...) {
+ va_list list;
+ va_start(list, format);
+ diag.error(format, list);
+ va_end(list);
+ })) {
+ return closure::LoadedFileInfo();
+ }
+
+ // if fat, remap just slice needed
+ bool fatButMissingSlice;
+ const FatFile* fh = (FatFile*)info.fileContent;
+ uint64_t sliceOffset = info.sliceOffset;
+ uint64_t sliceLen = info.sliceLen;
+ if ( fh->isFatFileWithSlice(diag, info.fileContentLen, reqArchName, sliceOffset, sliceLen, fatButMissingSlice) ) {
+ if ( (sliceOffset & 0xFFF) != 0 ) {
+ // slice not page aligned
+ if ( strncmp((char*)info.fileContent + sliceOffset, "!<arch>", 7) == 0 )
+ diag.error("file is static library");
+ else
+ diag.error("slice is not page aligned");
+ fileSystem.unloadFile(info);
+ return closure::LoadedFileInfo();
+ }
+ else {
+ // unmap anything before slice
+ fileSystem.unloadPartialFile(info, sliceOffset, sliceLen);
+ // Update the info to keep track of the new slice offset.
+ info.sliceOffset = sliceOffset;
+ info.sliceLen = sliceLen;
+ }
+ }
+ else if ( fatButMissingSlice ) {
+ diag.error("missing required arch %s in %s", reqArchName, path);
+ fileSystem.unloadFile(info);
+ return closure::LoadedFileInfo();
+ }
+
+ const MachOAnalyzer* mh = (MachOAnalyzer*)info.fileContent;
+
+ // validate is mach-o of requested arch and platform
+ if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, reqArchName, reqPlatform) ) {
+ fileSystem.unloadFile(info);
+ return closure::LoadedFileInfo();
+ }
+
+ // if has zero-fill expansion, re-map
+ mh = mh->remapIfZeroFill(diag, fileSystem, info);
+
+ // on error, remove mappings and return nullptr
+ if ( diag.hasError() ) {
+ fileSystem.unloadFile(info);
+ return closure::LoadedFileInfo();
+ }
+
+ // now that LINKEDIT is at expected offset, finish validation
+ mh->validLinkedit(diag, path);
+
+ // on error, remove mappings and return nullptr
+ if ( diag.hasError() ) {
+ fileSystem.unloadFile(info);
+ return closure::LoadedFileInfo();
+ }
+
+ return info;
+}
+
+#if DEBUG
+// only used in debug builds of cache builder to verify segment moves are valid
+void MachOAnalyzer::validateDyldCacheDylib(Diagnostics& diag, const char* path) const
+{
+ validLinkedit(diag, path);
+ validSegments(diag, path, 0xffffffff);
+}
+#endif
+
+uint64_t MachOAnalyzer::mappedSize() const
+{
+ const uint32_t pageSize = uses16KPages() ? 0x4000 : 0x1000;
+ __block uint64_t textSegVmAddr = 0;
+ __block uint64_t vmSpaceRequired = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ textSegVmAddr = info.vmAddr;
+ }
+ else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ vmSpaceRequired = info.vmAddr + ((info.vmSize + (pageSize-1)) & (-pageSize)) - textSegVmAddr;
+ stop = true;
+ }
+ });
+
+ return vmSpaceRequired;
+}
+
+bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const char* reqArchName, Platform reqPlatform) const
+{
+ // must start with mach-o magic value
+ if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) {
+ diag.error("could not use '%s' because it is not a mach-o file, 0x%08X", path, this->magic);
+ return false;
+ }
+
+ // must match requested architecture, if specified
+ if ( reqArchName != nullptr ) {
+ if ( !this->isArch(reqArchName)) {
+ // except when looking for x86_64h, fallback to x86_64
+ if ( (strcmp(reqArchName, "x86_64h") != 0) || !this->isArch("x86_64") ) {
+#if SUPPORT_ARCH_arm64e
+ // except when looking for arm64e, fallback to arm64
+ if ( (strcmp(reqArchName, "arm64e") != 0) || !this->isArch("arm64") ) {
+#endif
+ diag.error("could not use '%s' because it does not contain required architecture %s", path, reqArchName);
+ return false;
+#if SUPPORT_ARCH_arm64e
+ }
+#endif
+ }
+ }
+ }
+
+ // must be a filetype dyld can load
+ switch ( this->filetype ) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ break;
+ default:
+ diag.error("could not use '%s' because it is not a dylib, bundle, or executable", path);
+ return false;
+ }
+
+ // validate load commands structure
+ if ( !this->validLoadCommands(diag, path, sliceLength) ) {
+ return false;
+ }
+
+ // filter out static executables
+ if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) {
+ diag.error("could not use '%s' because it is a static executable", path);
+ return false;
+ }
+
+ // must match requested platform (do this after load commands are validated)
+ if ( !this->supportsPlatform(reqPlatform) ) {
+ diag.error("could not use '%s' because it was built for a different platform", path);
+ return false;
+ }
+
+ // validate dylib loads
+ if ( !validEmbeddedPaths(diag, path) )
+ return false;
+
+ // validate segments
+ if ( !validSegments(diag, path, sliceLength) )
+ return false;
+
+ // validate entry
+ if ( this->filetype == MH_EXECUTE ) {
+ if ( !validMain(diag, path) )
+ return false;
+ }
+
+ // further validations done in validLinkedit()
+
+ return true;
+}
+
+bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const
+{
+ // validate LINKEDIT layout
+ if ( !validLinkeditLayout(diag, path) )
+ return false;
+
+ if ( hasChainedFixups() ) {
+ if ( !validChainedFixupsInfo(diag, path) )
+ return false;
+ }
+ else {
+ // validate rebasing info
+ if ( !validRebaseInfo(diag, path) )
+ return false;
+
+ // validate binding info
+ if ( !validBindInfo(diag, path) )
+ return false;
+ }
+
+ return true;
+}
+
+bool MachOAnalyzer::validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const
+{
+ // check load command don't exceed file length
+ if ( this->sizeofcmds + sizeof(mach_header_64) > fileLen ) {
+ diag.error("in '%s' load commands exceed length of file", path);
+ return false;
+ }
+
+ // walk all load commands and sanity check them
+ Diagnostics walkDiag;
+ forEachLoadCommand(walkDiag, ^(const load_command* cmd, bool& stop) {});
+ if ( walkDiag.hasError() ) {
+#if BUILDING_CACHE_BUILDER
+ diag.error("in '%s' %s", path, walkDiag.errorMessage().c_str());
+#else
+ diag.error("in '%s' %s", path, walkDiag.errorMessage());
+#endif
+ return false;
+ }
+
+ // check load commands fit in TEXT segment
+ __block bool foundTEXT = false;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ foundTEXT = true;
+ if ( this->sizeofcmds + sizeof(mach_header_64) > info.fileSize ) {
+ diag.error("in '%s' load commands exceed length of __TEXT segment", path);
+ }
+ if ( info.fileOffset != 0 ) {
+ diag.error("in '%s' __TEXT segment not start of mach-o", path);
+ }
+ stop = true;
+ }
+ });
+ if ( !diag.noError() && !foundTEXT ) {
+ diag.error("in '%s' __TEXT segment not found", path);
+ return false;
+ }
+
+ return true;
+}
+
+const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const
+{
+ uint64_t vmSpaceRequired;
+ auto hasZeroFill = [this, &vmSpaceRequired]() {
+ __block bool hasZeroFill = false;
+ __block uint64_t textSegVmAddr = 0;
+ forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+ if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) {
+ textSegVmAddr = segmentInfo.vmAddr;
+ }
+ else if ( strcmp(segmentInfo.segName, "__LINKEDIT") == 0 ) {
+ uint64_t vmOffset = segmentInfo.vmAddr - textSegVmAddr;
+ // A zero fill page in the __DATA segment means the file offset of __LINKEDIT is less than its vm offset
+ if ( segmentInfo.fileOffset != vmOffset )
+ hasZeroFill = true;
+ vmSpaceRequired = segmentInfo.vmAddr + segmentInfo.vmSize - textSegVmAddr;
+ stop = true;
+ }
+ });
+ return hasZeroFill;
+ };
+
+ if (hasZeroFill()) {
+ vm_address_t newMappedAddr;
+ if ( ::vm_allocate(mach_task_self(), &newMappedAddr, (size_t)vmSpaceRequired, VM_FLAGS_ANYWHERE) != 0 ) {
+ diag.error("vm_allocate failure");
+ return nullptr;
+ }
+ // mmap() each segment read-only with standard layout
+ __block uint64_t textSegVmAddr;
+ forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+ if ( strcmp(segmentInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segmentInfo.vmAddr;
+ if ( segmentInfo.fileSize != 0 ) {
+ kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)((long)info.fileContent+segmentInfo.fileOffset), (vm_size_t)segmentInfo.fileSize, (vm_address_t)(newMappedAddr+segmentInfo.vmAddr-textSegVmAddr));
+ if ( r != KERN_SUCCESS ) {
+ diag.error("vm_copy() failure");
+ stop = true;
+ }
+ }
+ });
+ if ( diag.noError() ) {
+ // remove original mapping and return new mapping
+ fileSystem.unloadFile(info);
+
+ // Set vm_deallocate as the unload method.
+ info.unload = [](const closure::LoadedFileInfo& info) {
+ ::vm_deallocate(mach_task_self(), (vm_address_t)info.fileContent, (size_t)info.fileContentLen);
+ };
+
+ // And update the file content to the new location
+ info.fileContent = (const void*)newMappedAddr;
+ info.fileContentLen = vmSpaceRequired;
+ return (const MachOAnalyzer*)info.fileContent;
+ }
+ else {
+ // new mapping failed, return old mapping with an error in diag
+ ::vm_deallocate(mach_task_self(), newMappedAddr, (size_t)vmSpaceRequired);
+ return nullptr;
+ }
+ }
+
+ return this;
+}
+
+bool MachOAnalyzer::enforceFormat(Malformed kind) const
+{
+#if TARGET_OS_OSX
+ __block bool result = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platform == Platform::macOS ) {
+ switch (kind) {
+ case Malformed::linkeditOrder:
+ case Malformed::linkeditAlignment:
+ case Malformed::dyldInfoAndlocalRelocs:
+ // enforce these checks on new binaries only
+ result = (sdk >= 0x000A0E00); // macOS 10.14
+ }
+ }
+ });
+ // if binary is so old, there is no platform info, don't enforce malformed errors
+ return result;
+#else
+ return true;
+#endif
+}
+
+bool MachOAnalyzer::validEmbeddedPaths(Diagnostics& diag, const char* path) const
+{
+ __block int index = 1;
+ __block bool allGood = true;
+ __block bool foundInstallName = false;
+ __block int dependentsCount = 0;
+ 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.error("in '%s' load command #%d name offset (%u) outside its size (%u)", path, 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.error("in '%s' load command #%d string extends beyond end of load command", path, index);
+ stop = true;
+ allGood = false;
+ }
+ }
+ if ( cmd->cmd != LC_ID_DYLIB )
+ ++dependentsCount;
+ break;
+ case LC_RPATH:
+ rpathCmd = (rpath_command*)cmd;
+ if ( rpathCmd->path.offset > cmd->cmdsize ) {
+ diag.error("in '%s' load command #%d path offset (%u) outside its size (%u)", path, 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.error("in '%s' load command #%d string extends beyond end of load command", path, index);
+ stop = true;
+ allGood = false;
+ }
+ }
+ break;
+ }
+ ++index;
+ });
+ if ( !allGood )
+ return false;
+
+ if ( this->filetype == MH_DYLIB ) {
+ if ( !foundInstallName ) {
+ diag.error("in '%s' MH_DYLIB is missing LC_ID_DYLIB", path);
+ return false;
+ }
+ }
+ else {
+ if ( foundInstallName ) {
+ diag.error("in '%s' LC_ID_DYLIB found in non-MH_DYLIB", path);
+ return false;
+ }
+ }
+
+ if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE) ) {
+ diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path);
+ return false;
+ }
+
+ return true;
+}
+
+bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fileLen) const
+{
+ // 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.error("in '%s' load command size too small for LC_SEGMENT_64", path);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
+ diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
+ diag.error("in '%s' load command size 0x%X does not match nsects %d", path, cmd->cmdsize, seg->nsects);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen) ) {
+ diag.error("in '%s' segment load command content extends beyond end of file", path);
+ 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.error("in '%s' segment filesize exceeds vmsize", path);
+ 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.error("in '%s' load command size too small for LC_SEGMENT", path);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( (sectionsSpace % sizeof(section)) != 0 ) {
+ diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
+ badSegmentLoadCommand = true;
+ stop = true;
+ }
+ else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
+ diag.error("in '%s' load command size 0x%X does not match nsects %d", path, 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.error("in '%s' segment filesize exceeds vmsize", path);
+ 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 SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ if ( info.protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+ diag.error("in '%s' __TEXT segment permissions is not 'r-x'", path);
+ badPermissions = true;
+ stop = true;
+ }
+ hasTEXT = true;
+ }
+ else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ if ( info.protections != VM_PROT_READ ) {
+ diag.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path);
+ badPermissions = true;
+ stop = true;
+ }
+ hasLINKEDIT = true;
+ }
+ else if ( (info.protections & 0xFFFFFFF8) != 0 ) {
+ diag.error("in '%s' %s segment permissions has invalid bits set", path, info.segName);
+ badPermissions = true;
+ stop = true;
+ }
+ if ( greaterThanAddOrOverflow(info.fileOffset, info.fileSize, fileLen) ) {
+ diag.error("in '%s' %s segment content extends beyond end of file", path, info.segName);
+ badSize = true;
+ stop = true;
+ }
+ if ( is64() ) {
+ if ( info.vmAddr+info.vmSize < info.vmAddr ) {
+ diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+ badSize = true;
+ stop = true;
+ }
+ }
+ else {
+ if ( (uint32_t)(info.vmAddr+info.vmSize) < (uint32_t)(info.vmAddr) ) {
+ diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+ badSize = true;
+ stop = true;
+ }
+ }
+ });
+ if ( badPermissions || badSize )
+ return false;
+ if ( !hasTEXT ) {
+ diag.error("in '%s' missing __TEXT segment", path);
+ return false;
+ }
+ if ( !hasLINKEDIT ) {
+ diag.error("in '%s' missing __LINKEDIT segment", path);
+ return false;
+ }
+
+ // check for overlapping segments
+ __block bool badSegments = false;
+ forEachSegment(^(const SegmentInfo& info1, bool& stop1) {
+ uint64_t seg1vmEnd = info1.vmAddr + info1.vmSize;
+ uint64_t seg1FileEnd = info1.fileOffset + info1.fileSize;
+ forEachSegment(^(const SegmentInfo& info2, bool& stop2) {
+ if ( info1.segIndex == info2.segIndex )
+ return;
+ uint64_t seg2vmEnd = info2.vmAddr + info2.vmSize;
+ uint64_t seg2FileEnd = info2.fileOffset + info2.fileSize;
+ if ( ((info2.vmAddr <= info1.vmAddr) && (seg2vmEnd > info1.vmAddr) && (seg1vmEnd > info1.vmAddr )) || ((info2.vmAddr >= info1.vmAddr ) && (info2.vmAddr < seg1vmEnd) && (seg2vmEnd > info2.vmAddr)) ) {
+ diag.error("in '%s' segment %s vm range overlaps segment %s", path, info1.segName, info2.segName);
+ badSegments = true;
+ stop1 = true;
+ stop2 = true;
+ }
+ if ( ((info2.fileOffset <= info1.fileOffset) && (seg2FileEnd > info1.fileOffset) && (seg1FileEnd > info1.fileOffset)) || ((info2.fileOffset >= info1.fileOffset) && (info2.fileOffset < seg1FileEnd) && (seg2FileEnd > info2.fileOffset )) ) {
+ diag.error("in '%s' segment %s file content overlaps segment %s", path, info1.segName, info2.segName);
+ badSegments = true;
+ stop1 = true;
+ stop2 = true;
+ }
+ if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
+ if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset != 0)) ){
+ if ( !inDyldCache() ) {
+ // dyld cache __DATA_* segments are moved around
+ diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
+ 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.error("in '%s' section %s size too large 0x%llX", path, sect->sectname, sect->size);
+ badSections = true;
+ }
+ else if ( sect->addr < seg->vmaddr ) {
+ diag.error("in '%s' section %s start address 0x%llX is before containing segment's address 0x%0llX", path, sect->sectname, sect->addr, seg->vmaddr);
+ badSections = true;
+ }
+ else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+ diag.error("in '%s' section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", path, 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.error("in '%s' section %s size too large 0x%X", path, sect->sectname, sect->size);
+ badSections = true;
+ }
+ else if ( sect->addr < seg->vmaddr ) {
+ diag.error("in '%s' section %s start address 0x%X is before containing segment's address 0x%0X", path, sect->sectname, sect->addr, seg->vmaddr);
+ badSections = true;
+ }
+ else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+ diag.error("in '%s' section %s end address 0x%X is beyond containing segment's end address 0x%0X", path, sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+ badSections = true;
+ }
+ }
+ }
+ });
+
+ return !badSections;
+}
+
+
+bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
+{
+ __block uint64_t textSegStartAddr = 0;
+ __block uint64_t textSegStartSize = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ textSegStartAddr = info.vmAddr;
+ textSegStartSize = info.vmSize;
+ stop = true;
+ }
+ });
+
+ __block int mainCount = 0;
+ __block int threadCount = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ entry_point_command* mainCmd;
+ uint64_t startAddress;
+ switch (cmd->cmd) {
+ case LC_MAIN:
+ ++mainCount;
+ mainCmd = (entry_point_command*)cmd;
+ if ( mainCmd->entryoff > textSegStartSize ) {
+ diag.error("LC_MAIN points outside of __TEXT segment");
+ stop = true;
+ }
+ break;
+ case LC_UNIXTHREAD:
+ ++threadCount;
+ startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
+ if ( startAddress == 0 ) {
+ diag.error("LC_UNIXTHREAD not valid for arch %s", archName());
+ stop = true;
+ }
+ else if ( (startAddress < textSegStartAddr) || (startAddress > textSegStartAddr+textSegStartSize) ) {
+ diag.error("LC_UNIXTHREAD entry not in __TEXT segment");
+ stop = true;
+ }
+ break;
+ }
+ });
+ if ( diag.hasError() )
+ return false;
+ if ( diag.noError() && (mainCount+threadCount == 1) )
+ return true;
+
+ if ( mainCount + threadCount == 0 )
+ diag.error("missing LC_MAIN or LC_UNIXTHREAD");
+ else
+ diag.error("only one LC_MAIN or LC_UNIXTHREAD is allowed");
+ return false;
+}
+
+
+namespace {
+ struct LinkEditContentChunk
+ {
+ const char* name;
+ uint32_t stdOrder;
+ uint32_t fileOffsetStart;
+ uint32_t size;
+
+ static int compareByFileOffset(const void* l, const void* r) {
+ if ( ((LinkEditContentChunk*)l)->fileOffsetStart < ((LinkEditContentChunk*)r)->fileOffsetStart )
+ return -1;
+ else
+ return 1;
+ }
+ static int compareByStandardOrder(const void* l, const void* r) {
+ if ( ((LinkEditContentChunk*)l)->stdOrder < ((LinkEditContentChunk*)r)->stdOrder )
+ return -1;
+ else
+ return 1;
+ }
+ };
+} // anonymous namespace
+
+
+
+bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return false;
+ const uint32_t ptrSize = pointerSize();
+
+ // build vector of all blobs in LINKEDIT
+ LinkEditContentChunk blobs[32];
+ LinkEditContentChunk* bp = blobs;
+ if ( leInfo.dyldInfo != nullptr ) {
+ if ( leInfo.dyldInfo->rebase_size != 0 )
+ *bp++ = {"rebase opcodes", 1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size};
+ if ( leInfo.dyldInfo->bind_size != 0 )
+ *bp++ = {"bind opcodes", 2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size};
+ if ( leInfo.dyldInfo->weak_bind_size != 0 )
+ *bp++ = {"weak bind opcodes", 3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size};
+ if ( leInfo.dyldInfo->lazy_bind_size != 0 )
+ *bp++ = {"lazy bind opcodes", 4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size};
+ if ( leInfo.dyldInfo->export_size!= 0 )
+ *bp++ = {"exports trie", 5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size};
+ }
+ if ( leInfo.dynSymTab != nullptr ) {
+ if ( leInfo.dynSymTab->nlocrel != 0 )
+ *bp++ = {"local relocations", 6, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))};
+ if ( leInfo.dynSymTab->nextrel != 0 )
+ *bp++ = {"external relocations", 11, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))};
+ if ( leInfo.dynSymTab->nindirectsyms != 0 )
+ *bp++ = {"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4};
+ }
+ if ( leInfo.splitSegInfo != nullptr ) {
+ if ( leInfo.splitSegInfo->datasize != 0 )
+ *bp++ = {"shared cache info", 6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize};
+ }
+ if ( leInfo.functionStarts != nullptr ) {
+ if ( leInfo.functionStarts->datasize != 0 )
+ *bp++ = {"function starts", 7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize};
+ }
+ if ( leInfo.dataInCode != nullptr ) {
+ if ( leInfo.dataInCode->datasize != 0 )
+ *bp++ = {"data in code", 8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize};
+ }
+ if ( leInfo.symTab != nullptr ) {
+ if ( leInfo.symTab->nsyms != 0 )
+ *bp++ = {"symbol table", 10, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(ptrSize == 8 ? sizeof(nlist_64) : sizeof(struct nlist)))};
+ if ( leInfo.symTab->strsize != 0 )
+ *bp++ = {"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize};
+ }
+ if ( leInfo.codeSig != nullptr ) {
+ if ( leInfo.codeSig->datasize != 0 )
+ *bp++ = {"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) && enforceFormat(Malformed::dyldInfoAndlocalRelocs) ) {
+ diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path);
+ return false;
+ }
+ if ( leInfo.dynSymTab->nextrel != 0 ) {
+ diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path);
+ return false;
+ }
+ }
+ if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
+ diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path);
+ return false;
+ }
+ const unsigned long blobCount = bp - blobs;
+ if ( blobCount == 0 ) {
+ diag.error("in '%s' malformed mach-o misssing LINKEDIT", path);
+ return false;
+ }
+
+ uint32_t linkeditFileEnd = leInfo.layout.linkeditFileOffset + leInfo.layout.linkeditFileSize;
+
+
+ // sort blobs by file-offset and error on overlaps
+ ::qsort(blobs, blobCount, sizeof(LinkEditContentChunk), &LinkEditContentChunk::compareByFileOffset);
+ uint32_t prevEnd = leInfo.layout.linkeditFileOffset;
+ const char* prevName = "start of LINKEDIT";
+ for (unsigned long i=0; i < blobCount; ++i) {
+ const LinkEditContentChunk& blob = blobs[i];
+ if ( blob.fileOffsetStart < prevEnd ) {
+ diag.error("in '%s' LINKEDIT overlap of %s and %s", path, prevName, blob.name);
+ return false;
+ }
+ if (greaterThanAddOrOverflow(blob.fileOffsetStart, blob.size, linkeditFileEnd)) {
+ diag.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path, blob.name);
+ return false;
+ }
+ prevEnd = blob.fileOffsetStart + blob.size;
+ prevName = blob.name;
+ }
+
+ // sort vector by order and warn on non standard order or mis-alignment
+ ::qsort(blobs, blobCount, sizeof(LinkEditContentChunk), &LinkEditContentChunk::compareByStandardOrder);
+ prevEnd = leInfo.layout.linkeditFileOffset;
+ for (unsigned long i=0; i < blobCount; ++i) {
+ const LinkEditContentChunk& blob = blobs[i];
+ if ( ((blob.fileOffsetStart & (ptrSize-1)) != 0) && (blob.stdOrder != 20) && enforceFormat(Malformed::linkeditAlignment) ) // ok for "symbol table strings" to be mis-aligned
+ diag.error("in '%s' mis-aligned LINKEDIT content '%s'", path, blob.name);
+ if ( (blob.fileOffsetStart < prevEnd) && enforceFormat(Malformed::linkeditOrder) ) {
+ diag.error("in '%s' LINKEDIT out of order %s", path, blob.name);
+ }
+ prevEnd = blob.fileOffsetStart;
+ }
+
+ // Check for invalid symbol table sizes
+ if ( leInfo.symTab != nullptr ) {
+ if ( leInfo.symTab->nsyms > 0x10000000 ) {
+ diag.error("in '%s' malformed mach-o image: symbol table too large", path);
+ return false;
+ }
+ if ( leInfo.dynSymTab != nullptr ) {
+ // validate indirect symbol table
+ if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
+ if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table too large", path);
+ return false;
+ }
+ }
+ if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path);
+ return false;
+ }
+ if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path);
+ return false;
+ }
+ if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path);
+ return false;
+ }
+ if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path);
+ return false;
+ }
+ if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path);
+ return false;
+ }
+ if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym ) {
+ diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+
+bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const
+{
+ if ( !segIndexSet ) {
+ diag.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
+ return true;
+ }
+ if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
+ diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
+ return true;
+ }
+ if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+ diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
+ return true;
+ }
+ switch ( type ) {
+ case REBASE_TYPE_POINTER:
+ if ( !segments[segmentIndex].writable() ) {
+ diag.error("in '%s' %s pointer rebase is in non-writable segment", path, opcodeName);
+ return true;
+ }
+ if ( segments[segmentIndex].executable() ) {
+ diag.error("in '%s' %s pointer rebase is in executable segment", path, opcodeName);
+ return true;
+ }
+ break;
+ case REBASE_TYPE_TEXT_ABSOLUTE32:
+ case REBASE_TYPE_TEXT_PCREL32:
+ if ( !segments[segmentIndex].textRelocs ) {
+ diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName);
+ return true;
+ }
+ if ( segments[segmentIndex].writable() ) {
+ diag.error("in '%s' %s text rebase is in writable segment", path, opcodeName);
+ return true;
+ }
+ if ( !segments[segmentIndex].executable() ) {
+ diag.error("in '%s' %s pointer rebase is in non-executable segment", path, opcodeName);
+ return true;
+ }
+ break;
+ default:
+ diag.error("in '%s' %s unknown rebase type %d", path, opcodeName, type);
+ return true;
+ }
+ return false;
+}
+
+
+void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const
+{
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ segments[info.segIndex] = info;
+ });
+}
+
+
+bool MachOAnalyzer::validRebaseInfo(Diagnostics& diag, const char* path) const
+{
+ forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+ if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, type) )
+ stop = true;
+ });
+ return diag.noError();
+}
+
+
+void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+{
+ __block bool startVmAddrSet = false;
+ __block uint64_t startVmAddr = 0;
+ forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+ if ( type != REBASE_TYPE_TEXT_ABSOLUTE32 )
+ return;
+ if ( !startVmAddrSet ) {
+ for (int i=0; i <= segmentIndex; ++i) {
+ if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+ startVmAddr = segments[i].vmAddr;
+ startVmAddrSet = true;
+ break;
+ }
+ }
+ }
+ uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset;
+ uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
+ handler(runtimeOffset, stop);
+ });
+}
+
+
+void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+{
+ __block bool startVmAddrSet = false;
+ __block uint64_t startVmAddr = 0;
+ __block uint64_t lpVmAddr = 0;
+ __block uint64_t lpEndVmAddr = 0;
+ __block uint64_t shVmAddr = 0;
+ __block uint64_t shEndVmAddr = 0;
+ if ( ignoreLazyPointers ) {
+ forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+ lpVmAddr = info.sectAddr;
+ lpEndVmAddr = info.sectAddr + info.sectSize;
+ }
+ else if ( (info.sectFlags & S_ATTR_PURE_INSTRUCTIONS) && (strcmp(info.sectName, "__stub_helper") == 0) ) {
+ shVmAddr = info.sectAddr;
+ shEndVmAddr = info.sectAddr + info.sectSize;
+ }
+ });
+ }
+ forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+ if ( type != REBASE_TYPE_POINTER )
+ return;
+ if ( !startVmAddrSet ) {
+ for (int i=0; i < segmentIndex; ++i) {
+ if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+ startVmAddr = segments[i].vmAddr;
+ startVmAddrSet = true;
+ break;
+ }
+ }
+ }
+ uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset;
+ bool skipRebase = false;
+ if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) {
+ // rebase is in lazy pointer section
+ uint64_t lpValue = 0;
+ if ( ptrSize == 8 )
+ lpValue = *((uint64_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
+ else
+ lpValue = *((uint32_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
+ if ( (lpValue >= shVmAddr) && (lpValue < shEndVmAddr) ) {
+ // content is into stub_helper section
+ uint64_t lpTargetImageOffset = lpValue - startVmAddr;
+ const uint8_t* helperContent = (uint8_t*)this + lpTargetImageOffset;
+ bool isLazyStub = contentIsRegularStub(helperContent);
+ // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub
+ if ( isLazyStub )
+ skipRebase = true;
+ }
+ else {
+ // if lazy pointer does not point into stub_helper, then it points to weak-def symbol and we need rebase
+ }
+ }
+ if ( !skipRebase ) {
+ uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
+ handler(runtimeOffset, stop);
+ }
+ });
+}
+
+
+bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const
+{
+ switch (this->cputype) {
+ case CPU_TYPE_X86_64:
+ return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xE9) ); // push $xxx / JMP pcRel
+ case CPU_TYPE_I386:
+ return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xFF) && (helperContent[2] == 0x26) ); // push $xxx / JMP *pcRel
+ case CPU_TYPE_ARM:
+ return ( (helperContent[0] == 0x00) && (helperContent[1] == 0xC0) && (helperContent[2] == 0x9F) && (helperContent[3] == 0xE5) ); // ldr ip, [pc, #0]
+ case CPU_TYPE_ARM64:
+ return ( (helperContent[0] == 0x50) && (helperContent[1] == 0x00) && (helperContent[2] == 0x00) && (helperContent[3] == 0x18) ); // ldr w16, L0
+
+ }
+ return false;
+}
+
+static int uint32Sorter(const void* l, const void* r) {
+ if ( *((uint32_t*)l) < *((uint32_t*)r) )
+ return -1;
+ else
+ return 1;
+}
+
+
+void MachOAnalyzer::forEachRebase(Diagnostics& diag,
+ void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+ uint8_t type, bool& stop)) const
+{
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+ getAllSegmentsInfos(diag, segmentsInfo);
+ if ( diag.hasError() )
+ return;
+
+ if ( leInfo.dyldInfo != nullptr ) {
+ const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+ const uint8_t* end = p + leInfo.dyldInfo->rebase_size;
+ const uint32_t ptrSize = pointerSize();
+ 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*ptrSize;
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+ segOffset += ptrSize;
+ if ( stop )
+ break;
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(diag, p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+ segOffset += ptrSize;
+ if ( stop )
+ break;
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+ segOffset += read_uleb128(diag, p, end) + ptrSize;
+ 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) {
+ handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+ segOffset += skip + ptrSize;
+ if ( stop )
+ break;
+ }
+ break;
+ default:
+ diag.error("unknown rebase opcode 0x%02X", opcode);
+ }
+ }
+ }
+ else {
+ // old binary, walk relocations
+ const uint64_t relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+ 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);
+ const uint8_t ptrSize = pointerSize();
+ STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, relocAddrs, 2048);
+ 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;
+ }
+ relocAddrs.push_back(reloc->r_address);
+ }
+ if ( !relocAddrs.empty() ) {
+ ::qsort(&relocAddrs[0], relocAddrs.count(), sizeof(uint32_t), &uint32Sorter);
+ for (uint32_t addrOff : relocAddrs) {
+ uint32_t segIndex = 0;
+ uint64_t segOffset = 0;
+ if ( segIndexAndOffsetForAddress(relocsStartAddress+addrOff, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+ uint8_t type = REBASE_TYPE_POINTER;
+ if ( this->cputype == CPU_TYPE_I386 ) {
+ if ( segmentsInfo[segIndex].executable() )
+ type = REBASE_TYPE_TEXT_ABSOLUTE32;
+ }
+ handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, type , stop);
+ }
+ else {
+ diag.error("local relocation has out of range r_address");
+ break;
+ }
+ }
+ }
+ // then process indirect symbols
+ forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+ if ( bind )
+ return;
+ uint32_t segIndex = 0;
+ uint64_t segOffset = 0;
+ if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+ handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, REBASE_TYPE_POINTER, indStop);
+ }
+ else {
+ diag.error("local relocation has out of range r_address");
+ indStop = true;
+ }
+ });
+ }
+}
+
+bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const
+{
+ for (uint32_t i=0; i < segCount; ++i) {
+ if ( (segmentsInfos[i].vmAddr <= addr) && (addr < segmentsInfos[i].vmAddr+segmentsInfos[i].vmSize) ) {
+ segIndex = i;
+ segOffset = addr - segmentsInfos[i].vmAddr;
+ return true;
+ }
+ }
+ return false;
+}
+
+uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
+{
+ if ( is64() ) {
+ // x86_64 reloc base address is first writable segment
+ for (uint32_t i=0; i < segCount; ++i) {
+ if ( segmentsInfos[i].writable() )
+ return segmentsInfos[i].vmAddr;
+ }
+ }
+ return segmentsInfos[0].vmAddr;
+}
+
+
+
+void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint64_t pointerAddress, 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 ptrSize = pointerSize();
+ 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 dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectionStop) {
+ uint8_t sectionType = (sectInfo.sectFlags & SECTION_TYPE);
+ bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.sectFlags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->cputype == CPU_TYPE_I386);
+ if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && !selfModifyingStub )
+ return;
+ 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 ? sectInfo.reserved2 : ptrSize;
+ uint32_t elementCount = (uint32_t)(sectInfo.sectSize/elementSize);
+ if ( greaterThanAddOrOverflow(sectInfo.reserved1, elementCount, indirectSymbolTableCount) ) {
+ diag.error("section %s overflows indirect symbol table", sectInfo.sectName);
+ sectionStop = true;
+ return;
+ }
+
+ for (uint32_t i=0; (i < elementCount) && !stop; ++i) {
+ uint32_t symNum = indirectSymbolTable[sectInfo.reserved1 + i];
+ if ( symNum == INDIRECT_SYMBOL_ABS )
+ continue;
+ if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
+ handler(sectInfo.sectAddr+i*elementSize, false, 0, "", false, false, false, stop);
+ continue;
+ }
+ if ( symNum > symCount ) {
+ diag.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo.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", sectInfo.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(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
+ }
+ sectionStop = stop;
+ });
+}
+
+int MachOAnalyzer::libOrdinalFromDesc(uint16_t n_desc) const
+{
+ // -flat_namespace is always flat lookup
+ if ( (this->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 MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const
+{
+ forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+ uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+ uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+ if ( invalidBindState(diag, opcodeName, path, leInfo, segments, segIndexSet, libraryOrdinalSet, dylibCount,
+ libOrdinal, ptrSize, segmentIndex, segmentOffset, type, symbolName) ) {
+ stop = true;
+ }
+ }, ^(const char* symbolName) {
+ });
+ return diag.noError();
+}
+
+bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint32_t ptrSize,
+ uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
+{
+ if ( !segIndexSet ) {
+ diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
+ return true;
+ }
+ if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
+ diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
+ return true;
+ }
+ if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+ diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
+ return true;
+ }
+ if ( symbolName == NULL ) {
+ diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path, opcodeName);
+ return true;
+ }
+ if ( !libraryOrdinalSet ) {
+ diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", path, opcodeName);
+ return true;
+ }
+ if ( libOrdinal > (int)dylibCount ) {
+ diag.error("in '%s' %s has library ordinal too large (%d) max (%d)", path, opcodeName, libOrdinal, dylibCount);
+ return true;
+ }
+ if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+ diag.error("in '%s' %s has unknown library special ordinal (%d)", path, opcodeName, libOrdinal);
+ return true;
+ }
+ switch ( type ) {
+ case BIND_TYPE_POINTER:
+ if ( !segments[segmentIndex].writable() ) {
+ diag.error("in '%s' %s pointer bind is in non-writable segment", path, opcodeName);
+ return true;
+ }
+ if ( segments[segmentIndex].executable() ) {
+ diag.error("in '%s' %s pointer bind is in executable segment", path, opcodeName);
+ return true;
+ }
+ break;
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ case BIND_TYPE_TEXT_PCREL32:
+ if ( !segments[segmentIndex].textRelocs ) {
+ diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName);
+ return true;
+ }
+ if ( segments[segmentIndex].writable() ) {
+ diag.error("in '%s' %s text bind is in writable segment", path, opcodeName);
+ return true;
+ }
+ if ( !segments[segmentIndex].executable() ) {
+ diag.error("in '%s' %s pointer bind is in non-executable segment", path, opcodeName);
+ return true;
+ }
+ break;
+ default:
+ diag.error("in '%s' %s unknown bind type %d", path, opcodeName, type);
+ return true;
+ }
+ return false;
+}
+
+void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+ bool weakImport, uint64_t addend, bool& stop),
+ void (^strongHandler)(const char* symbolName)) const
+{
+ __block bool startVmAddrSet = false;
+ __block uint64_t startVmAddr = 0;
+ forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+ uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+ uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+ if ( !startVmAddrSet ) {
+ for (int i=0; i <= segmentIndex; ++i) {
+ if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+ startVmAddr = segments[i].vmAddr;
+ startVmAddrSet = true;
+ break;
+ }
+ }
+ }
+ uint64_t bindVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
+ uint64_t runtimeOffset = bindVmOffset - startVmAddr;
+ handler(runtimeOffset, libOrdinal, symbolName, weakImport, addend, stop);
+ }, ^(const char* symbolName) {
+ strongHandler(symbolName);
+ });
+}
+
+void MachOAnalyzer::forEachBind(Diagnostics& diag,
+ void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+ uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+ uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop),
+ void (^strongHandler)(const char* symbolName)) const
+{
+ const uint32_t ptrSize = this->pointerSize();
+ bool stop = false;
+
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+ getAllSegmentsInfos(diag, segmentsInfo);
+ 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;
+ 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;
+ 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:
+ stop = 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:
+ handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += ptrSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += read_uleb128(diag, p, end) + ptrSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += immediate*ptrSize + ptrSize;
+ 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) {
+ handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += skip + ptrSize;
+ if ( stop )
+ break;
+ }
+ break;
+ default:
+ diag.error("bad bind opcode 0x%02X", *p);
+ }
+ }
+ if ( diag.hasError() )
+ 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:
+ handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += ptrSize;
+ 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;
+ }
+ }
+ }
+ if ( diag.hasError() )
+ return;
+
+ // process weak bind info
+ if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
+ p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
+ end = p + leInfo.dyldInfo->weak_bind_size;
+ type = BIND_TYPE_POINTER;
+ segmentOffset = 0;
+ segmentIndex = 0;
+ symbolName = NULL;
+ libraryOrdinal = BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE;
+ segIndexSet = false;
+ libraryOrdinalSet= true;
+ 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:
+ stop = 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_bind");
+ 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;
+ if ( immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION ) {
+ strongHandler(symbolName);
+ }
+ 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:
+ handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += ptrSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += read_uleb128(diag, p, end) + ptrSize;
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += immediate*ptrSize + ptrSize;
+ 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) {
+ handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+ ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+ segmentOffset += skip + ptrSize;
+ if ( stop )
+ break;
+ }
+ break;
+ default:
+ diag.error("bad bind opcode 0x%02X", *p);
+ }
+ }
+ }
+ }
+ else {
+ // old binary, process external relocations
+ const uint64_t relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+ const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
+ const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nextrel];
+ bool is64Bit = is64() ;
+ const uint8_t relocSize = (is64Bit ? 3 : 2);
+ 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;
+ 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;
+ }
+ uint32_t segIndex = 0;
+ uint64_t segOffset = 0;
+ if ( segIndexAndOffsetForAddress(relocsStartAddress+reloc->r_address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+ uint32_t symbolIndex = reloc->r_symbolnum;
+ if ( symbolIndex > symCount ) {
+ diag.error("external relocation has out of range r_symbolnum");
+ break;
+ }
+ else {
+ uint32_t strOffset = is64Bit ? symbols64[symbolIndex].n_un.n_strx : symbols32[symbolIndex].n_un.n_strx;
+ uint16_t n_desc = is64Bit ? symbols64[symbolIndex].n_desc : symbols32[symbolIndex].n_desc;
+ uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+ if ( strOffset >= poolSize ) {
+ diag.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex);
+ break;
+ }
+ else {
+ const char* symbolName = stringPool + strOffset;
+ bool weakImport = (n_desc & N_WEAK_REF);
+ const uint8_t* content = (uint8_t*)this + segmentsInfo[segIndex].vmAddr - leInfo.layout.textUnslidVMAddr + segOffset;
+ uint64_t addend = is64Bit ? *((uint64_t*)content) : *((uint32_t*)content);
+ handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal,
+ ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, symbolName, weakImport, addend, stop);
+ }
+ }
+ }
+ else {
+ diag.error("local relocation has out of range r_address");
+ break;
+ }
+ }
+ // then process indirect symbols
+ forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+ if ( !bind )
+ return;
+ uint32_t segIndex = 0;
+ uint64_t segOffset = 0;
+ if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+ handler("indirect symbol", leInfo, segmentsInfo, true, true, dylibCount, bindLibOrdinal,
+ ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, bindSymbolName, bindWeakImport, 0, indStop);
+ }
+ else {
+ diag.error("indirect symbol has out of range address");
+ indStop = true;
+ }
+ });
+ }
+
+}
+
+
+bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path) const
+{
+ __block uint32_t maxTargetCount = 0;
+ __block uint32_t currentTargetCount = 0;
+ forEachChainedFixup(diag,
+ ^(uint32_t totalTargets, bool& stop) {
+ maxTargetCount = totalTargets;
+ },
+ ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ if ( symbolName == NULL ) {
+ diag.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path);
+ }
+ else if ( !libraryOrdinalSet ) {
+ diag.error("in '%s' missing BIND_OPCODE_SET_DYLIB_ORDINAL", path);
+ }
+ else if ( libOrdinal > (int)dylibCount ) {
+ diag.error("in '%s' has library ordinal too large (%d) max (%d)", path, libOrdinal, dylibCount);
+ }
+ else if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+ diag.error("in '%s' has unknown library special ordinal (%d)", path, libOrdinal);
+ }
+ else if ( type != BIND_TYPE_POINTER ) {
+ diag.error("in '%s' unknown bind type %d", path, type);
+ }
+ else if ( currentTargetCount > maxTargetCount ) {
+ diag.error("in '%s' chained target counts exceeds BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB", path);
+ }
+ ++currentTargetCount;
+ if ( diag.hasError() )
+ stop = true;
+ },
+ ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop) {
+ if ( !segIndexSet ) {
+ diag.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path);
+ }
+ else if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
+ diag.error("in '%s' segment index %d too large", path, segmentIndex);
+ }
+ else if ( segmentOffset > (segments[segmentIndex].vmSize-8) ) {
+ diag.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path, segmentOffset, segments[segmentIndex].vmSize);
+ }
+ else if ( !segments[segmentIndex].writable() ) {
+ diag.error("in '%s' pointer bind is in non-writable segment", path);
+ }
+ else if ( segments[segmentIndex].executable() ) {
+ diag.error("in '%s' pointer bind is in executable segment", path);
+ }
+ if ( diag.hasError() )
+ stop = true;
+ }
+ );
+
+ return diag.noError();
+}
+
+
+void MachOAnalyzer::forEachChainedFixup(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
+ void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
+ void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop)) const
+{
+ bool stop = false;
+
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return;
+
+ BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+ getAllSegmentsInfos(diag, segmentsInfo);
+ 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;
+ 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;
+ uint64_t targetTableCount;
+ uint64_t addend = 0;
+ bool weakImport = 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:
+ stop = 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_SEGMENT_AND_OFFSET_ULEB:
+ segmentIndex = immediate;
+ segmentOffset = read_uleb128(diag, p, end);
+ segIndexSet = true;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(diag, p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( addTarget )
+ addTarget(leInfo, segmentsInfo, libraryOrdinalSet, dylibCount, libraryOrdinal, type, symbolName, addend, weakImport, stop);
+ break;
+ case BIND_OPCODE_THREADED:
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ targetTableCount = read_uleb128(diag, p, end);
+ if ( targetTableCount > 65535 ) {
+ diag.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large");
+ stop = true;
+ }
+ else {
+ if ( targetCount )
+ targetCount((uint32_t)targetTableCount, stop);
+ }
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY:
+ if ( addChainStart )
+ addChainStart(leInfo, segmentsInfo, segmentIndex, segIndexSet, segmentOffset, stop);
+ break;
+ default:
+ diag.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate);
+ }
+ break;
+ default:
+ diag.error("bad bind opcode 0x%02X", immediate);
+ }
+ }
+ if ( diag.hasError() )
+ return;
+ }
+}
+
+void MachOAnalyzer::forEachChainedFixupStart(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const
+{
+ __block bool startVmAddrSet = false;
+ __block uint64_t startVmAddr = 0;
+ forEachChainedFixup(diag, nullptr, nullptr, ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop) {
+ if ( !startVmAddrSet ) {
+ for (int i=0; i <= segmentIndex; ++i) {
+ if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+ startVmAddr = segments[i].vmAddr;
+ startVmAddrSet = true;
+ break;
+ }
+ }
+ }
+ uint64_t startVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
+ uint64_t runtimeOffset = startVmOffset - startVmAddr;
+ callback((uint32_t)runtimeOffset, stop);
+ });
+}
+
+void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const
+{
+ forEachChainedFixup(diag, nullptr, ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount,
+ int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop){
+ callback(libOrdinal, symbolName, addend, weakImport, stop);
+ }, nullptr);
+}
+
+uint32_t MachOAnalyzer::segmentCount() const
+{
+ __block uint32_t count = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ ++count;
+ });
+ return count;
+}
+
+bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
+{
+ fileOffset = 0;
+ size = 0;
+
+ 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
+
+ // early exist if no LC_CODE_SIGNATURE
+ if ( fileOffset == 0 )
+ return false;
+
+ // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+ __block bool goodSignature = true;
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) {
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
+ goodSignature = false;
+ });
+ }
+
+ return goodSignature;
+}
+
+bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const
+{
+ __block bool result = false;
+ forEachInitializer(diag, contentRebased, ^(uint32_t offset) {
+ result = true;
+ }, dyldCache);
+ return result;
+}
+
+void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache) const
+{
+ __block uint64_t prefTextSegAddrStart = 0;
+ __block uint64_t prefTextSegAddrEnd = 0;
+
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ prefTextSegAddrStart = info.vmAddr;
+ prefTextSegAddrEnd = info.vmAddr + info.vmSize;
+ stop = true;
+ }
+ });
+ if ( prefTextSegAddrStart == prefTextSegAddrEnd ) {
+ diag.error("no __TEXT segment");
+ return;
+ }
+ uint64_t slide = (long)this - prefTextSegAddrStart;
+
+ // 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 ( (prefTextSegAddrStart < dashInit) && (dashInit < prefTextSegAddrEnd) )
+ callback((uint32_t)(dashInit - prefTextSegAddrStart));
+ 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 ( (prefTextSegAddrStart < dashInit) && (dashInit < prefTextSegAddrEnd) )
+ callback((uint32_t)(dashInit - prefTextSegAddrStart));
+ else
+ diag.error("-init does not point within __TEXT segment");
+ }
+ });
+
+ // next any function pointers in mod-init section
+ unsigned ptrSize = pointerSize();
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (info.sectFlags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+ const uint8_t* content;
+ content = (uint8_t*)(info.sectAddr + slide);
+ if ( (info.sectSize % ptrSize) != 0 ) {
+ diag.error("initializer section %s/%s has bad size", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( malformedSectionRange ) {
+ diag.error("initializer section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( ((long)content % ptrSize) != 0 ) {
+ diag.error("initializer section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( ptrSize == 8 ) {
+ const uint64_t* initsStart = (uint64_t*)content;
+ const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + info.sectSize);
+ for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
+ uint64_t anInit = *p;
+ if ( contentRebased )
+ anInit -= slide;
+ if ( hasChainedFixups() ) {
+ ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
+ if ( aChainedInit->authBind.bind )
+ diag.error("initializer uses bind");
+ if ( aChainedInit->authRebase.auth ) {
+ anInit = aChainedInit->authRebase.target;
+ }
+ else {
+ anInit = aChainedInit->plainRebase.signExtendedTarget();
+ }
+ }
+ if ( (anInit <= prefTextSegAddrStart) || (anInit > prefTextSegAddrEnd) ) {
+ diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit);
+ stop = true;
+ break;
+ }
+ callback((uint32_t)(anInit - prefTextSegAddrStart));
+ }
+ }
+ else {
+ const uint32_t* initsStart = (uint32_t*)content;
+ const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + info.sectSize);
+ for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
+ uint32_t anInit = *p;
+ if ( contentRebased )
+ anInit -= slide;
+ if ( (anInit <= prefTextSegAddrStart) || (anInit > prefTextSegAddrEnd) ) {
+ diag.error("initializer 0x%0X does not point within __TEXT segment", anInit);
+ stop = true;
+ break;
+ }
+ callback(anInit - (uint32_t)prefTextSegAddrStart);
+ }
+ }
+ }
+ });
+}
+
+
+void MachOAnalyzer::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
+}
+
+
+bool MachOAnalyzer::hasObjC() const
+{
+ __block bool result = false;
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
+ result = true;
+ stop = true;
+ }
+ if ( (this->cputype == CPU_TYPE_I386) && (strcmp(info.sectName, "__image_info") == 0) && (strcmp(info.segInfo.segName, "__OBJC") == 0) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const
+{
+ __block bool result = false;
+ if ( (this->cputype == CPU_TYPE_I386) && supportsPlatform(Platform::macOS) ) {
+ // old objc runtime has no special section for +load methods, scan for string
+ int64_t slide = getSlide();
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+ if ( malformedSectionRange ) {
+ diag.error("cstring section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
+ const char* s = (char*)content;
+ const char* end = s + info.sectSize;
+ while ( s < end ) {
+ if ( strcmp(s, "load") == 0 ) {
+ result = true;
+ stop = true;
+ return;
+ }
+ while (*s != '\0' )
+ ++s;
+ ++s;
+ }
+ }
+ });
+ }
+ else {
+ // in new objc runtime compiler puts classes/categories with +load method in specical section
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
+ return;
+ if ( (strcmp(info.sectName, "__objc_nlclslist") == 0) || (strcmp(info.sectName, "__objc_nlcatlist") == 0)) {
+ result = true;
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+ return nullptr;
+
+ size = leInfo.dyldInfo->rebase_size;
+ return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+}
+
+const void* MachOAnalyzer::getBindOpcodes(uint32_t& size) const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+ return nullptr;
+
+ size = leInfo.dyldInfo->bind_size;
+ return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
+}
+
+const void* MachOAnalyzer::getLazyBindOpcodes(uint32_t& size) const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+ return nullptr;
+
+ size = leInfo.dyldInfo->lazy_bind_size;
+ return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
+}
+
+
+uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex, uint64_t targetSegOffset) const
+{
+ __block uint64_t textVmAddr = 0;
+ __block uint64_t result = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 )
+ textVmAddr = info.vmAddr;
+ if ( info.segIndex == targetSegIndex ) {
+ result = (info.vmAddr - textVmAddr) + targetSegOffset;
+ }
+ });
+ return result;
+}
+
+bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const
+{
+ size = 0;
+ forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+ runtimeOffset = (uint32_t)(info.sectAddr - preferredLoadAddress());
+ size = (uint32_t)info.sectSize;
+ stop = true;
+ }
+ });
+ return (size != 0);
+}
+
+uint64_t MachOAnalyzer::preferredLoadAddress() const
+{
+ __block uint64_t textVmAddr = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ textVmAddr = info.vmAddr;
+ stop = true;
+ }
+ });
+ return textVmAddr;
+}
+
+
+bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const
+{
+ 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;
+ uint64_t startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
+ offset = (uint32_t)(startAddress - preferredLoadAddress());
+ }
+ });
+ return (offset != 0);
+}
+
+uint64_t MachOAnalyzer::entryAddrFromThreadCmd(const thread_command* cmd) const
+{
+ assert(cmd->cmd == LC_UNIXTHREAD);
+ const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
+ const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+ uint64_t startAddress = 0;
+ switch ( this->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;
+ }
+ return startAddress;
+}
+
+
+void MachOAnalyzer::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
+{
+ const unsigned ptrSize = pointerSize();
+ const unsigned entrySize = 2 * ptrSize;
+ forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) ) {
+ if ( info.sectSize % entrySize != 0 ) {
+ diag.error("interposing section %s/%s has bad size", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( malformedSectionRange ) {
+ diag.error("interposing section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( (info.sectAddr % ptrSize) != 0 ) {
+ diag.error("interposing section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ handler(info.sectAddr - preferredLoadAddress(), info.sectSize, stop);
+ }
+ });
+}
+
+void MachOAnalyzer::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+ forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ( (info.sectFlags & SECTION_TYPE) == S_DTRACE_DOF ) && !malformedSectionRange ) {
+ callback((uint32_t)(info.sectAddr - info.segInfo.vmAddr));
+ }
+ });
+}
+
+bool MachOAnalyzer::getCDHash(uint8_t cdHash[20]) const
+{
+ 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 MachOAnalyzer::isRestricted() const
+{
+ __block bool result = false;
+ forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( (strcmp(info.segInfo.segName, "__RESTRICT") == 0) && (strcmp(info.sectName, "__restrict") == 0) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool MachOAnalyzer::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 MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
+{
+ __block bool retval = true;
+
+ // only dylibs can go in cache
+ if ( (this->filetype != MH_DYLIB) && (this->filetype != MH_BUNDLE) ) {
+ retval = false;
+ failureReason("not MH_DYLIB or MH_BUNDLE");
+ }
+
+ // flat namespace files cannot go in cache
+ if ( (this->flags & MH_TWOLEVEL) == 0 ) {
+ retval = false;
+ failureReason("not built with two level namespaces");
+ }
+
+ // can only depend on other dylibs with absolute paths
+ __block bool allDepPathsAreGood = true;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( loadPath[0] != '/' ) {
+ allDepPathsAreGood = false;
+ stop = true;
+ }
+ });
+ if ( !allDepPathsAreGood ) {
+ retval = false;
+ failureReason("depends on dylibs that are not absolute paths");
+ }
+
+ // dylibs with interposing info cannot have dlopen closure pre-computed
+ __block bool hasInterposing = false;
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
+ hasInterposing = true;
+ });
+ if ( hasInterposing ) {
+ retval = false;
+ failureReason("has interposing tuples");
+ }
+
+ // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
+ Diagnostics diag;
+ auto checkBind = ^(int libOrdinal, bool& stop) {
+ switch (libOrdinal) {
+ case BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE:
+ failureReason("has weak externals");
+ retval = false;
+ stop = true;
+ break;
+ case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+ failureReason("has dynamic_lookup binds");
+ retval = false;
+ stop = true;
+ break;
+ case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+ failureReason("has reference to main executable (bundle loader)");
+ retval = false;
+ stop = true;
+ break;
+ }
+ };
+
+ if (hasChainedFixups()) {
+ forEachChainedFixupTarget(diag, ^(int libOrdinal, const char *symbolName, uint64_t addend, bool weakImport, bool &stop) {
+ checkBind(libOrdinal, stop);
+ });
+ } else {
+ forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+ checkBind(libOrdinal, stop);
+ },
+ ^(const char* symbolName) {
+ });
+ }
+
+ // special system dylib overrides cannot have closure pre-computed
+ if ( strncmp(path, "/usr/lib/system/introspection/", 30) == 0 ) {
+ retval = false;
+ failureReason("override of OS dylib");
+ }
+
+ return retval;
+}
+
+bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
+{
+ if (!MachOFile::canBePlacedInDyldCache(path, failureReason))
+ return false;
+ if ( !(isArch("x86_64") || isArch("x86_64h")) )
+ return true;
+
+ // Kick dylibs out of the x86_64 cache if they are using TBI.
+ __block bool rebasesOk = true;
+ Diagnostics diag;
+ uint64_t startVMAddr = preferredLoadAddress();
+ uint64_t endVMAddr = startVMAddr + mappedSize();
+ forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
+ uint64_t value = *(uint64_t*)((uint8_t*)this + runtimeOffset);
+ if ( (value < startVMAddr) || (value >= endVMAddr) ) {
+ failureReason("rebase value out of range of dylib");
+ rebasesOk = false;
+ stop = true;
+ }
+ });
+ return rebasesOk;
+}
+
+} // 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 MachOAnalyzer_h
+#define MachOAnalyzer_h
+
+
+#define BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE (-3)
+
+#include "MachOLoaded.h"
+#include "ClosureFileSystem.h"
+
+namespace dyld3 {
+
+// Extra functionality on loaded mach-o files only used during closure building
+struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
+{
+ static closure::LoadedFileInfo load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* logicalPath, const char* reqArchName, Platform reqPlatform);
+ static const MachOAnalyzer* validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength, const char* reqArchName, Platform reqPlatform);
+
+ bool validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const char* reqArchName, Platform reqPlatform) const;
+ uint64_t mappedSize() const;
+ bool hasObjC() const;
+ bool hasPlusLoadMethod(Diagnostics& diag) const;
+ uint64_t preferredLoadAddress() 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;
+
+ bool isEncrypted() const;
+ bool getCDHash(uint8_t cdHash[20]) const;
+ bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+ bool usesLibraryValidation() const;
+ bool isRestricted() const;
+ bool getEntry(uint32_t& offset, bool& usesCRT) const;
+ bool isSlideable() const;
+ bool hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache=nullptr) const;
+ void forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) 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 forEachRebase(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, 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)(uint64_t pointerAddress, bool bind, int bindLibOrdinal,
+ const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const;
+ void forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const;
+ const void* content(uint64_t vmOffset);
+ void forEachLocalReloc(void (^handler)(uint64_t runtimeOffset, bool& stop)) const;
+ void forEachExternalReloc(void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool& stop)) const;
+
+ const void* getRebaseOpcodes(uint32_t& size) const;
+ const void* getBindOpcodes(uint32_t& size) const;
+ const void* getLazyBindOpcodes(uint32_t& size) const;
+ uint64_t segAndOffsetToRuntimeOffset(uint8_t segIndex, uint64_t segOffset) const;
+ bool hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const;
+ void forEachRebase(Diagnostics& diag, bool ignoreLazyPointer, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+ void forEachTextRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+ void forEachBind(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+ bool weakImport, uint64_t addend, bool& stop),
+ void (^strongHandler)(const char* symbolName)) const;
+ void forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const;
+ void forEachChainedFixupStart(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+ bool canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
+ bool canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
+
+#if DEBUG
+ void validateDyldCacheDylib(Diagnostics& diag, const char* path) const;
+#endif
+
+ const MachOAnalyzer* remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const;
+
+ // protected members of subclass promoted to public here
+ using MachOLoaded::SegmentInfo;
+ using MachOLoaded::SectionInfo;
+ using MachOLoaded::forEachSegment;
+ using MachOLoaded::forEachSection;
+ using MachOLoaded::forEachDependentDylib;
+ using MachOLoaded::getDylibInstallName;
+ using MachOLoaded::FoundSymbol;
+ using MachOLoaded::findExportedSymbol;
+
+private:
+
+ struct SegmentStuff
+ {
+ uint64_t fileOffset;
+ uint64_t fileSize;
+ uint64_t writable : 1,
+ executable : 1,
+ textRelocsAllowed : 1, // segment supports text relocs (i386 only)
+ segSize : 61;
+ };
+
+ enum class Malformed { linkeditOrder, linkeditAlignment, dyldInfoAndlocalRelocs };
+ bool enforceFormat(Malformed) const;
+
+ const uint8_t* getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
+
+ bool validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const;
+ bool validEmbeddedPaths(Diagnostics& diag, const char* path) const;
+ bool validSegments(Diagnostics& diag, const char* path, size_t fileLen) const;
+ bool validLinkedit(Diagnostics& diag, const char* path) const;
+ bool validLinkeditLayout(Diagnostics& diag, const char* path) const;
+ bool validRebaseInfo(Diagnostics& diag, const char* path) const;
+ bool validBindInfo(Diagnostics& diag, const char* path) const;
+ bool validMain(Diagnostics& diag, const char* path) const;
+ bool validChainedFixupsInfo(Diagnostics& diag, const char* path) const;
+
+ bool invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const;
+ bool invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ 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 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;
+
+ void getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const;
+ bool segmentHasTextRelocs(uint32_t segIndex) const;
+ uint64_t relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
+ void forEachRebase(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop)) const;
+ bool segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const;
+ void forEachBind(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+ uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
+ uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop),
+ void (^strongHandler)(const char* symbolName)) const;
+ void forEachChainedFixup(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
+ void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
+ void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop)) const;
+ bool contentIsRegularStub(const uint8_t* helperContent) const;
+ uint64_t entryAddrFromThreadCmd(const thread_command* cmd) const;
+
+};
+
+} // namespace dyld3
+
+#endif /* MachOAnalyzer_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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <TargetConditionals.h>
+#include <mach/host_info.h>
+#include <mach/mach.h>
+#include <mach/mach_host.h>
+
+#include "MachOFile.h"
+#include "SupportedArchs.h"
+
+#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 CPU_SUBTYPE_ARM64_32_V8
+ #define CPU_SUBTYPE_ARM64_32_V8 1
+#endif
+
+#ifndef CPU_TYPE_ARM64_32
+ #ifndef CPU_ARCH_ABI64_32
+ #define CPU_ARCH_ABI64_32 0x02000000
+ #endif
+ #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
+#endif
+
+namespace dyld3 {
+
+//////////////////////////// FatFile ////////////////////////////////////////
+
+const FatFile* FatFile::isFatFile(const void* fileStart)
+{
+ const FatFile* fileStartAsFat = (FatFile*)fileStart;
+ if ( (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC)) || (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
+ return fileStartAsFat;
+ else
+ return nullptr;
+}
+
+void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const
+{
+ if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+ diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
+ return;
+ }
+ bool stop = false;
+ const fat_arch* const archs = (fat_arch*)(((char*)this)+sizeof(fat_header));
+ for (uint32_t i=0; i < OSSwapBigToHostInt32(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*)this+offset, len, stop);
+ if ( stop )
+ break;
+ }
+ }
+ else if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC_64) ) {
+ if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+ diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
+ return;
+ }
+ bool stop = false;
+ const fat_arch_64* const archs = (fat_arch_64*)(((char*)this)+sizeof(fat_header));
+ for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
+ uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
+ uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
+ uint64_t offset = OSSwapBigToHostInt64(archs[i].offset);
+ uint64_t len = OSSwapBigToHostInt64(archs[i].size);
+ if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
+ diag.error("slice %d extends beyond end of file", i);
+ return;
+ }
+ callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
+ if ( stop )
+ break;
+ }
+ }
+ else {
+ diag.error("not a fat file");
+ }
+}
+
+bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
+{
+ missingSlice = false;
+ if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
+ return false;
+
+ __block bool found = false;
+ forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+ const char* sliceArchName = MachOFile::archName(sliceCpuType, sliceCpuSubType);
+ if ( strcmp(sliceArchName, archName) == 0 ) {
+ sliceOffset = (char*)sliceStart - (char*)this;
+ 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 && (strcmp(archName, "x86_64h") == 0) )
+ return isFatFileWithSlice(diag, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
+
+ return found;
+}
+
+
+//////////////////////////// MachOFile ////////////////////////////////////////
+
+
+const MachOFile::ArchInfo MachOFile::_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 },
+#if SUPPORT_ARCH_arm64e
+ { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E },
+#endif
+#if SUPPORT_ARCH_arm64_32
+ { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
+#endif
+ { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
+ { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
+ { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
+};
+
+const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
+ { "macOS", Platform::macOS, LC_VERSION_MIN_MACOSX },
+ { "iOS", Platform::iOS, LC_VERSION_MIN_IPHONEOS },
+ { "tvOS", Platform::tvOS, LC_VERSION_MIN_TVOS },
+ { "watchOS", Platform::watchOS, LC_VERSION_MIN_WATCHOS },
+ { "bridgeOS", Platform::bridgeOS, LC_BUILD_VERSION },
+ { "iOSMac", Platform::iOSMac, LC_BUILD_VERSION },
+ { "iOS-sim", Platform::iOS_simulator, LC_BUILD_VERSION },
+ { "tvOS-sim", Platform::tvOS_simulator, LC_BUILD_VERSION },
+ { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION },
+};
+
+
+bool MachOFile::is64() const
+{
+ return (this->magic == MH_MAGIC_64);
+}
+
+uint32_t MachOFile::pointerSize() const
+{
+ if (this->magic == MH_MAGIC_64)
+ return 8;
+ else
+ return 4;
+}
+
+bool MachOFile::uses16KPages() const
+{
+ switch (this->cputype) {
+ case CPU_TYPE_ARM64:
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_ARM64_32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOFile::isArch(const char* aName) const
+{
+ return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
+}
+
+const char* MachOFile::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 MachOFile::cpuTypeFromArchName(const char* archName)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( strcmp(archName, info.name) == 0 ) {
+ return info.cputype;
+ }
+ }
+ return 0;
+}
+
+uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( strcmp(archName, info.name) == 0 ) {
+ return info.cpusubtype;
+ }
+ }
+ return 0;
+}
+
+const char* MachOFile::archName() const
+{
+ return archName(this->cputype, this->cpusubtype);
+}
+
+static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
+{
+ if ( num >= place ) {
+ unsigned dig = (num/place);
+ *s++ = '0' + dig;
+ num -= (dig*place);
+ startedPrinting = true;
+ }
+ else if ( startedPrinting ) {
+ *s++ = '0';
+ }
+}
+
+static void appendNumber(char*& s, unsigned num)
+{
+ assert(num < 99999);
+ bool startedPrinting = false;
+ appendDigit(s, num, 10000, startedPrinting);
+ appendDigit(s, num, 1000, startedPrinting);
+ appendDigit(s, num, 100, startedPrinting);
+ appendDigit(s, num, 10, startedPrinting);
+ appendDigit(s, num, 1, startedPrinting);
+ if ( !startedPrinting )
+ *s++ = '0';
+}
+
+void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
+{
+ // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
+ char* s = versionString;
+ appendNumber(s, (packedVersion >> 16));
+ *s++ = '.';
+ appendNumber(s, (packedVersion >> 8) & 0xFF);
+ *s++ = '.';
+ appendNumber(s, (packedVersion & 0xFF));
+ *s++ = '\0';
+}
+
+bool MachOFile::supportsPlatform(Platform reqPlatform) const
+{
+ __block bool foundRequestedPlatform = false;
+ __block bool foundOtherPlatform = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platform == reqPlatform )
+ foundRequestedPlatform = true;
+ else
+ foundOtherPlatform = true;
+ });
+ if ( foundRequestedPlatform )
+ return true;
+
+ // we did find some platform info, but not requested, so return false
+ if ( foundOtherPlatform )
+ return false;
+
+ // binary has no explict load command to mark platform
+ // could be an old macOS binary, look at arch
+ if ( reqPlatform == Platform::macOS ) {
+ if ( this->cputype == CPU_TYPE_X86_64 )
+ return true;
+ if ( this->cputype == CPU_TYPE_I386 )
+ return true;
+ }
+
+ return false;
+}
+
+Platform MachOFile::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
+}
+
+#if __x86_64__
+static bool isHaswell()
+{
+ // FIXME: figure out a commpage way to check this
+ static bool sAlreadyDetermined = false;
+ static bool sHaswell = false;
+ if ( !sAlreadyDetermined ) {
+ 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);
+ sHaswell = (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
+ sAlreadyDetermined = true;
+ }
+ return sHaswell;
+}
+#endif
+
+const char* MachOFile::currentArchName()
+{
+#if __ARM_ARCH_7K__
+ return "armv7k";
+#elif __ARM_ARCH_7A__
+ return "armv7";
+#elif __ARM_ARCH_7S__
+ return "armv7s";
+#elif __arm64e__
+ return "arm64e";
+#elif __arm64__
+#if __LP64__
+ return "arm64";
+#else
+ return "arm64_32";
+#endif
+#elif __x86_64__
+ return isHaswell() ? "x86_64h" : "x86_64";
+#elif __i386__
+ return "i386";
+#else
+ #error unknown arch
+#endif
+}
+
+
+bool MachOFile::isDylib() const
+{
+ return (this->filetype == MH_DYLIB);
+}
+
+bool MachOFile::isBundle() const
+{
+ return (this->filetype == MH_BUNDLE);
+}
+
+bool MachOFile::isMainExecutable() const
+{
+ return (this->filetype == MH_EXECUTE);
+}
+
+bool MachOFile::isDynamicExecutable() const
+{
+ if ( this->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 MachOFile::isPIE() const
+{
+ return (this->flags & MH_PIE);
+}
+
+const char* MachOFile::platformName(Platform reqPlatform)
+{
+ for (const PlatformInfo& info : _s_platformInfos) {
+ if ( info.platform == reqPlatform )
+ return info.name;
+ }
+ return "unknown platform";
+}
+
+void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ const build_version_command* buildCmd = (build_version_command *)cmd;
+ const version_min_command* versCmd = (version_min_command*)cmd;
+ switch ( cmd->cmd ) {
+ case LC_BUILD_VERSION:
+ handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ handler(Platform::macOS, versCmd->version, versCmd->sdk);
+ break;
+ case LC_VERSION_MIN_IPHONEOS:
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+ handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::iOS, versCmd->version, versCmd->sdk);
+ break;
+ case LC_VERSION_MIN_TVOS:
+ if ( this->cputype == CPU_TYPE_X86_64 )
+ handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::tvOS, versCmd->version, versCmd->sdk);
+ break;
+ case LC_VERSION_MIN_WATCHOS:
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+ handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::watchOS, versCmd->version, versCmd->sdk);
+ break;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+
+bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
+{
+ if ( !hasMachOMagic() ) {
+ diag.error("file does not start with MH_MAGIC[_64]");
+ return false;
+ }
+ if ( this->sizeofcmds + sizeof(mach_header_64) > fileSize ) {
+ diag.error("load commands exceed length of first segment");
+ return false;
+ }
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
+ return diag.noError();
+}
+
+bool MachOFile::hasMachOMagic() const
+{
+ return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
+}
+
+void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
+{
+ bool stop = false;
+ const load_command* startCmds = nullptr;
+ if ( this->magic == MH_MAGIC_64 )
+ startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
+ else if ( this->magic == MH_MAGIC )
+ startCmds = (load_command*)((char *)this + sizeof(mach_header));
+ else {
+ diag.error("file does not start with MH_MAGIC[_64]");
+ return; // not a mach-o file
+ }
+ const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
+ const load_command* cmd = startCmds;
+ for (uint32_t i = 0; i < this->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;
+ }
+}
+
+const char* MachOFile::installName() const
+{
+ const char* name;
+ uint32_t compatVersion;
+ uint32_t currentVersion;
+ if ( getDylibInstallName(&name, &compatVersion, ¤tVersion) )
+ return name;
+ return nullptr;
+}
+
+bool MachOFile::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;
+}
+
+bool MachOFile::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;
+}
+
+void MachOFile::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;
+ 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 MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
+ const dylinker_command* envCmd = (dylinker_command*)cmd;
+ const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
+ // only process variables that start with DYLD_ and end in _PATH
+ if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
+ const char* equals = strchr(keyEqualsValue, '=');
+ if ( equals != NULL ) {
+ if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
+ callback(keyEqualsValue, stop);
+ }
+ }
+ }
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+bool MachOFile::enforceCompatVersion() const
+{
+ __block bool result = true;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ switch ( platform ) {
+ case Platform::macOS:
+ if ( minOS >= 0x000A0E00 ) // macOS 10.14
+ result = false;
+ break;
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ if ( minOS >= 0x000C0000 ) // iOS 12.0
+ result = false;
+ break;
+ case Platform::watchOS:
+ case Platform::watchOS_simulator:
+ if ( minOS >= 0x00050000 ) // watchOS 5.0
+ result = false;
+ break;
+ case Platform::bridgeOS:
+ if ( minOS >= 0x00030000 ) // bridgeOS 3.0
+ result = false;
+ break;
+ case Platform::iOSMac:
+ result = false;
+ break;
+ case Platform::unknown:
+ break;
+ }
+ });
+ return result;
+}
+
+
+void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
+{
+ Diagnostics diag;
+ const bool intel32 = (this->cputype == CPU_TYPE_I386);
+ __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;
+ }
+ SegmentInfo info;
+ info.fileOffset = segCmd->fileoff;
+ info.fileSize = segCmd->filesize;
+ info.vmAddr = segCmd->vmaddr;
+ info.vmSize = segCmd->vmsize;
+ info.sizeOfSections = sizeOfSections;
+ info.segName = segCmd->segname;
+ info.protections = segCmd->initprot;
+ info.textRelocs = false;
+ info.p2align = p2align;
+ info.segIndex = segIndex;
+ callback(info, stop);
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ bool hasTextRelocs = false;
+ 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;
+ if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+ hasTextRelocs = true;
+ }
+ SegmentInfo info;
+ info.fileOffset = segCmd->fileoff;
+ info.fileSize = segCmd->filesize;
+ info.vmAddr = segCmd->vmaddr;
+ info.vmSize = segCmd->vmsize;
+ info.sizeOfSections = sizeOfSections;
+ info.segName = segCmd->segname;
+ info.protections = segCmd->initprot;
+ info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
+ info.p2align = p2align;
+ info.segIndex = segIndex;
+ callback(info, stop);
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
+{
+ Diagnostics diag;
+ const bool intel32 = (this->cputype == CPU_TYPE_I386);
+ __block uint32_t segIndex = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ SectionInfo sectInfo;
+ 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;
+ }
+ sectInfo.segInfo.fileOffset = segCmd->fileoff;
+ sectInfo.segInfo.fileSize = segCmd->filesize;
+ sectInfo.segInfo.vmAddr = segCmd->vmaddr;
+ sectInfo.segInfo.vmSize = segCmd->vmsize;
+ sectInfo.segInfo.sizeOfSections = sizeOfSections;
+ sectInfo.segInfo.segName = segCmd->segname;
+ sectInfo.segInfo.protections = segCmd->initprot;
+ sectInfo.segInfo.textRelocs = false;
+ sectInfo.segInfo.p2align = p2align;
+ sectInfo.segInfo.segIndex = segIndex;
+ 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 malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+ sectInfo.sectName = sectName;
+ sectInfo.sectFileOffset = sect->offset;
+ sectInfo.sectFlags = sect->flags;
+ sectInfo.sectAddr = sect->addr;
+ sectInfo.sectSize = sect->size;
+ sectInfo.sectAlignP2 = sect->align;
+ sectInfo.reserved1 = sect->reserved1;
+ sectInfo.reserved2 = sect->reserved2;
+ callback(sectInfo, malformedSectionRange, stop);
+ }
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ bool hasTextRelocs = false;
+ 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;
+ if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+ hasTextRelocs = true;
+ }
+ sectInfo.segInfo.fileOffset = segCmd->fileoff;
+ sectInfo.segInfo.fileSize = segCmd->filesize;
+ sectInfo.segInfo.vmAddr = segCmd->vmaddr;
+ sectInfo.segInfo.vmSize = segCmd->vmsize;
+ sectInfo.segInfo.sizeOfSections = sizeOfSections;
+ sectInfo.segInfo.segName = segCmd->segname;
+ sectInfo.segInfo.protections = segCmd->initprot;
+ sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
+ sectInfo.segInfo.p2align = p2align;
+ sectInfo.segInfo.segIndex = segIndex;
+ 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 malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+ sectInfo.sectName = sectName;
+ sectInfo.sectFileOffset = sect->offset;
+ sectInfo.sectFlags = sect->flags;
+ sectInfo.sectAddr = sect->addr;
+ sectInfo.sectSize = sect->size;
+ sectInfo.sectAlignP2 = sect->align;
+ sectInfo.reserved1 = sect->reserved1;
+ sectInfo.reserved2 = sect->reserved2;
+ callback(sectInfo, malformedSectionRange, stop);
+ }
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+bool MachOFile::hasWeakDefs() const
+{
+ return (this->flags & MH_WEAK_DEFINES);
+}
+
+bool MachOFile::hasThreadLocalVariables() const
+{
+ return (this->flags & MH_HAS_TLV_DESCRIPTORS);
+}
+
+static bool endsWith(const char* str, const char* suffix)
+{
+ size_t strLen = strlen(str);
+ size_t suffixLen = strlen(suffix);
+ if ( strLen < suffixLen )
+ return false;
+ return (strcmp(&str[strLen-suffixLen], suffix) == 0);
+}
+
+bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
+{
+ // only dylibs can go in cache
+ if ( this->filetype != MH_DYLIB ) {
+ failureReason("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
+ bool retval = true;
+ const char* dylibName = installName();
+ if ( dylibName[0] != '/' ) {
+ retval = false;
+ failureReason("install name not an absolute path");
+ }
+ else if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
+ retval = false;
+ failureReason("Not in '/usr/lib/' or '/System/Library/'");
+ }
+
+ // flat namespace files cannot go in cache
+ if ( (this->flags & MH_TWOLEVEL) == 0 ) {
+ retval = false;
+ failureReason("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;
+ failureReason("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;
+ failureReason("Missing split seg info");
+ }
+ if ( !hasDyldInfo ) {
+ retval = false;
+ failureReason("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;
+ failureReason("Depends on dylibs ineligable for dyld cache");
+ }
+
+ // dylibs with interposing info cannot be in cache
+ __block bool hasInterposing = false;
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
+ hasInterposing = true;
+ });
+ if ( hasInterposing ) {
+ retval = false;
+ failureReason("Has interposing tuples");
+ }
+
+ return retval;
+}
+
+
+bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+ if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
+ if ( encCmd->cryptid == 1 ) {
+ // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
+ textOffset = encCmd->cryptoff;
+ size = encCmd->cryptsize;
+ return true;
+ }
+ }
+ textOffset = 0;
+ size = 0;
+ return false;
+}
+
+bool MachOFile::canBeFairPlayEncrypted() const
+{
+ return (findFairPlayEncryptionLoadCommand() != nullptr);
+}
+
+const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
+{
+ __block const encryption_info_command* result = nullptr;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+ result = (encryption_info_command*)cmd;
+ stop = true;
+ }
+ });
+ if ( diag.noError() )
+ return result;
+ else
+ return nullptr;
+}
+
+
+bool MachOFile::hasChainedFixups() const
+{
+#if SUPPORT_ARCH_arm64e
+ // for now only arm64e uses chained fixups
+ return ( strcmp(archName(), "arm64e") == 0 );
+#else
+ return false;
+#endif
+}
+
+uint64_t MachOFile::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 MachOFile::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 |= (~0ULL) << bit;
+ 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 MachOFile_h
+#define MachOFile_h
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <uuid/uuid.h>
+
+#include "Diagnostics.h"
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+ #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+
+#ifndef PLATFORM_IOSSIMULATOR
+ #define PLATFORM_IOSSIMULATOR (7)
+#endif
+
+#ifndef PLATFORM_TVOSSIMULATOR
+ #define PLATFORM_TVOSSIMULATOR (8)
+#endif
+
+#ifndef PLATFORM_WATCHOSSIMULATOR
+ #define PLATFORM_WATCHOSSIMULATOR (9)
+#endif
+
+
+namespace dyld3 {
+
+
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+inline 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>
+inline bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
+ return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+
+// Note, this should match PLATFORM_* values in <mach-o/loader.h>
+enum class Platform {
+ unknown = 0,
+ macOS = 1, // PLATFORM_MACOS
+ iOS = 2, // PLATFORM_IOS
+ tvOS = 3, // PLATFORM_TVOS
+ watchOS = 4, // PLATFORM_WATCHOS
+ bridgeOS = 5, // PLATFORM_BRIDGEOS
+ iOSMac = 6, // PLATFORM_IOSMAC
+ iOS_simulator = 7, // PLATFORM_IOSSIMULATOR
+ tvOS_simulator = 8, // PLATFORM_TVOSSIMULATOR
+ watchOS_simulator = 9 // PLATFORM_WATCHOSSIMULATOR
+};
+
+// A file read/mapped into memory
+struct VIS_HIDDEN FatFile : fat_header
+{
+ static const FatFile* isFatFile(const void* fileContent);
+ void forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const;
+ bool isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
+};
+
+
+// A mach-o file read/mapped into memory
+// Only info from mach_header or load commands is accessible (no LINKEDIT info)
+struct VIS_HIDDEN MachOFile : mach_header
+{
+ static const char* archName(uint32_t cputype, uint32_t cpusubtype);
+ static const char* platformName(Platform platform);
+ static uint32_t cpuTypeFromArchName(const char* archName);
+ static uint32_t cpuSubtypeFromArchName(const char* archName);
+ static void packedVersionToString(uint32_t packedVersion, char versionString[32]);
+ static const char* currentArchName();
+ static Platform currentPlatform();
+ 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);
+
+
+ bool hasMachOMagic() const;
+ bool isMachO(Diagnostics& diag, uint64_t fileSize) const;
+ bool isDylib() const;
+ bool isBundle() const;
+ bool isMainExecutable() const;
+ bool isDynamicExecutable() const;
+ bool isPIE() const;
+ bool isArch(const char* archName) const;
+ const char* archName() const;
+ bool is64() const;
+ uint32_t pointerSize() const;
+ bool uses16KPages() const;
+ bool supportsPlatform(Platform) const;
+ bool isSimulatorBinary() const;
+ bool getUuid(uuid_t uuid) const;
+ bool hasWeakDefs() const;
+ bool hasThreadLocalVariables() const;
+ bool getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
+ void forEachSupportedPlatform(void (^callback)(Platform platform, uint32_t minOS, uint32_t sdk)) const;
+ const char* installName() const; // returns nullptr is no install name
+ void forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
+ bool canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
+ bool canBeFairPlayEncrypted() const;
+ bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
+ bool hasChainedFixups() const;
+ void forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const;
+ bool enforceCompatVersion() const;
+
+ struct SegmentInfo
+ {
+ uint64_t fileOffset;
+ uint64_t fileSize;
+ uint64_t vmAddr;
+ uint64_t vmSize;
+ uint64_t sizeOfSections;
+ const char* segName;
+ uint32_t protections;
+ uint32_t textRelocs : 1, // segment has text relocs (i386 only)
+ segIndex : 15,
+ p2align : 16;
+ bool readable() const { return protections & VM_PROT_READ; }
+ bool writable() const { return protections & VM_PROT_WRITE; }
+ bool executable() const { return protections & VM_PROT_EXECUTE; }
+ };
+
+ struct SectionInfo
+ {
+ SegmentInfo segInfo;
+ uint64_t sectAddr;
+ uint64_t sectSize;
+ const char* sectName;
+ uint32_t sectFileOffset;
+ uint32_t sectFlags;
+ uint32_t sectAlignP2;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ };
+
+ void forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const;
+ void forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const;
+
+protected:
+ void forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
+
+ const encryption_info_command* findFairPlayEncryptionLoadCommand() const;
+
+ struct ArchInfo
+ {
+ const char* name;
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ };
+ static const ArchInfo _s_archInfos[];
+
+ struct PlatformInfo
+ {
+ const char* name;
+ Platform platform;
+ uint32_t loadCommand;
+ };
+ static const PlatformInfo _s_platformInfos[];
+};
+
+
+} // namespace dyld3
+
+#endif /* MachOFile_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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <mach-o/reloc.h>
+#include <mach-o/nlist.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include <stdio.h>
+
+#include "MachOLoaded.h"
+#include "MachOFile.h"
+#include "MachOFile.h"
+#include "CodeSigningTypes.h"
+
+
+#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 {
+
+
+void MachOLoaded::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 hasMinVersion = 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 ( hasMinVersion )
+ diag.error("multiple LC_VERSION_MIN_* load commands");
+ hasMinVersion = 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 ( hasMinVersion )
+ diag.error("LC_BUILD_VERSION cannot coexist LC_VERSION_MIN_* with load commands");
+ 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 MachOLoaded::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const
+{
+ getLinkEditLoadCommands(diag, result);
+ if ( diag.noError() )
+ getLayoutInfo(result.layout);
+}
+
+void MachOLoaded::getLayoutInfo(LayoutInfo& result) const
+{
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ result.textUnslidVMAddr = (uintptr_t)info.vmAddr;
+ result.slide = (uintptr_t)(((uint64_t)this) - info.vmAddr);
+ }
+ else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ result.linkeditUnslidVMAddr = (uintptr_t)info.vmAddr;
+ result.linkeditFileOffset = (uint32_t)info.fileOffset;
+ result.linkeditFileSize = (uint32_t)info.fileSize;
+ result.linkeditSegIndex = info.segIndex;
+ }
+ });
+}
+
+bool MachOLoaded::hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const
+{
+ runtimeOffset = 0;
+ size = 0;
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ if ( diag.hasError() )
+ return false;
+ if ( leInfo.dyldInfo != nullptr ) {
+ uint32_t offsetInLinkEdit = leInfo.dyldInfo->export_off - leInfo.layout.linkeditFileOffset;
+ runtimeOffset = offsetInLinkEdit + (uint32_t)(leInfo.layout.linkeditUnslidVMAddr - leInfo.layout.textUnslidVMAddr);
+ size = leInfo.dyldInfo->export_size;
+ return true;
+ }
+ return false;
+}
+
+
+#if BUILDING_LIBDYLD
+// this is only used by dlsym() at runtime. All other binding is done when the closure is built.
+bool MachOLoaded::hasExportedSymbol(const char* symbolName, DependentToMachOLoaded finder, void** result,
+ bool* resultPointsToInstructions) const
+{
+ typedef void* (*ResolverFunc)(void);
+ ResolverFunc resolver;
+ Diagnostics diag;
+ FoundSymbol foundInfo;
+ if ( findExportedSymbol(diag, symbolName, foundInfo, finder) ) {
+ switch ( foundInfo.kind ) {
+ case FoundSymbol::Kind::headerOffset: {
+ *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value;
+ *resultPointsToInstructions = false;
+ int64_t slide = foundInfo.foundInDylib->getSlide();
+ foundInfo.foundInDylib->forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ uint64_t sectStartAddr = sectInfo.sectAddr + slide;
+ uint64_t sectEndAddr = sectStartAddr + sectInfo.sectSize;
+ if ( ((uint64_t)*result >= sectStartAddr) && ((uint64_t)*result < sectEndAddr) ) {
+ *resultPointsToInstructions = (sectInfo.sectFlags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.sectFlags & S_ATTR_SOME_INSTRUCTIONS);
+ stop = true;
+ }
+ });
+ break;
+ }
+ case FoundSymbol::Kind::absolute:
+ *result = (void*)(long)foundInfo.value;
+ *resultPointsToInstructions = false;
+ 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)();
+ // FIXME: Set this properly
+ *resultPointsToInstructions = true;
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+#endif // BUILDING_LIBDYLD
+
+bool MachOLoaded::findExportedSymbol(Diagnostics& diag, const char* symbolName, FoundSymbol& foundInfo, DependentToMachOLoaded 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 ) {
+ if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+ if ( depMH->findExportedSymbol(diag, symbolName, foundInfo, findDependent) ) {
+ stop = true;
+ foundInReExportedDylib = true;
+ }
+ }
+ }
+ ++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;
+ if ( (ordinal == 0) || (ordinal > dependentDylibCount()) ) {
+ diag.error("re-export ordinal %lld out of range for %s", ordinal, symbolName);
+ return false;
+ }
+ uint32_t depIndex = (uint32_t)(ordinal-1);
+ if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+ return depMH->findExportedSymbol(diag, importedName, 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.isWeakDef = false;
+ foundInfo.foundInDylib = this;
+ 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;
+ }
+ if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
+ foundInfo.isWeakDef = true;
+ 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;
+ 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 = this;
+ foundInfo.value = n_value - leInfo.layout.textUnslidVMAddr;
+ foundInfo.resolverFuncOffset = 0;
+ foundInfo.foundSymbolName = symbolName;
+ stop = true;
+ }
+ });
+ if ( foundInfo.foundInDylib == nullptr ) {
+ // symbol not exported from this image. Search any re-exported dylibs
+ __block unsigned depIndex = 0;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( isReExport && findDependent ) {
+ if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+ if ( depMH->findExportedSymbol(diag, symbolName, foundInfo, findDependent) ) {
+ stop = true;
+ }
+ }
+ }
+ ++depIndex;
+ });
+ }
+ return (foundInfo.foundInDylib != nullptr);
+ }
+}
+
+intptr_t MachOLoaded::getSlide() const
+{
+ Diagnostics diag;
+ __block intptr_t slide = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* seg = (segment_command_64*)cmd;
+ if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+ slide = (uintptr_t)(((uint64_t)this) - seg->vmaddr);
+ stop = true;
+ }
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* seg = (segment_command*)cmd;
+ if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+ slide = (uintptr_t)(((uint64_t)this) - seg->vmaddr);
+ stop = true;
+ }
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return slide;
+}
+
+const uint8_t* MachOLoaded::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const
+{
+ uint32_t offsetInLinkedit = fileOffset - info.linkeditFileOffset;
+ uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide;
+ return (uint8_t*)(linkeditStartAddr + offsetInLinkedit);
+}
+
+
+void MachOLoaded::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*)symbols;
+ 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 MachOLoaded::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);
+ }
+ }
+ }
+}
+
+uint32_t MachOLoaded::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* MachOLoaded::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;
+}
+
+const char* MachOLoaded::segmentName(uint32_t targetSegIndex) const
+{
+ __block const char* result = nullptr;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( targetSegIndex == info.segIndex ) {
+ result = info.segName;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool MachOLoaded::findClosestFunctionStart(uint64_t address, uint64_t* functionStartAddress) const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return false;
+ if ( leInfo.functionStarts == nullptr )
+ return false;
+
+ const uint8_t* starts = getLinkEditContent(leInfo.layout, leInfo.functionStarts->dataoff);
+ const uint8_t* startsEnd = starts + leInfo.functionStarts->datasize;
+
+ uint64_t lastAddr = (uint64_t)(long)this;
+ uint64_t runningAddr = lastAddr;
+ while (diag.noError()) {
+ uint64_t value = read_uleb128(diag, starts, startsEnd);
+ if ( value == 0 )
+ break;
+ lastAddr = runningAddr;
+ runningAddr += value;
+ //fprintf(stderr, " addr=0x%08llX\n", runningAddr);
+ if ( runningAddr > address ) {
+ *functionStartAddress = lastAddr;
+ return true;
+ }
+ };
+
+ return false;
+}
+
+bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, uint64_t* symbolAddr) const
+{
+ Diagnostics diag;
+ LinkEditInfo leInfo;
+ getLinkEditPointers(diag, leInfo);
+ if ( diag.hasError() )
+ return false;
+ if ( (leInfo.symTab == nullptr) || (leInfo.dynSymTab == nullptr) )
+ return false;
+ uint64_t targetUnslidAddress = address - leInfo.layout.slide;
+
+ 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));
+ if ( is64() ) {
+ const struct nlist_64* symbols64 = (struct nlist_64*)symbols;
+ const struct nlist_64* bestSymbol = nullptr;
+ // first walk all global symbols
+ const struct nlist_64* const globalsStart = &symbols64[leInfo.dynSymTab->iextdefsym];
+ const struct nlist_64* const globalsEnd = &globalsStart[leInfo.dynSymTab->nextdefsym];
+ for (const struct nlist_64* s = globalsStart; s < globalsEnd; ++s) {
+ if ( (s->n_type & N_TYPE) == N_SECT ) {
+ if ( bestSymbol == nullptr ) {
+ if ( s->n_value <= targetUnslidAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ // next walk all local symbols
+ const struct nlist_64* const localsStart = &symbols64[leInfo.dynSymTab->ilocalsym];
+ const struct nlist_64* const localsEnd = &localsStart[leInfo.dynSymTab->nlocalsym];
+ for (const struct nlist_64* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+ if ( bestSymbol == nullptr ) {
+ if ( s->n_value <= targetUnslidAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ if ( bestSymbol != NULL ) {
+ *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+ if ( bestSymbol->n_un.n_strx < maxStringOffset )
+ *symbolName = &stringPool[bestSymbol->n_un.n_strx];
+ return true;
+ }
+ }
+ else {
+ const struct nlist* bestSymbol = nullptr;
+ // first walk all global symbols
+ const struct nlist* const globalsStart = &symbols[leInfo.dynSymTab->iextdefsym];
+ const struct nlist* const globalsEnd = &globalsStart[leInfo.dynSymTab->nextdefsym];
+ for (const struct nlist* s = globalsStart; s < globalsEnd; ++s) {
+ if ( (s->n_type & N_TYPE) == N_SECT ) {
+ if ( bestSymbol == nullptr ) {
+ if ( s->n_value <= targetUnslidAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ // next walk all local symbols
+ const struct nlist* const localsStart = &symbols[leInfo.dynSymTab->ilocalsym];
+ const struct nlist* const localsEnd = &localsStart[leInfo.dynSymTab->nlocalsym];
+ for (const struct nlist* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+ if ( bestSymbol == nullptr ) {
+ if ( s->n_value <= targetUnslidAddress )
+ bestSymbol = s;
+ }
+ else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+ bestSymbol = s;
+ }
+ }
+ }
+ if ( bestSymbol != nullptr ) {
+#if __arm__
+ if ( bestSymbol->n_desc & N_ARM_THUMB_DEF )
+ *symbolAddr = (bestSymbol->n_value | 1) + leInfo.layout.slide;
+ else
+ *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+#else
+ *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+#endif
+ if ( bestSymbol->n_un.n_strx < maxStringOffset )
+ *symbolName = &stringPool[bestSymbol->n_un.n_strx];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const void* MachOLoaded::findSectionContent(const char* segName, const char* sectName, uint64_t& size) const
+{
+ __block const void* result = nullptr;
+ forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(sectInfo.sectName, sectName) == 0) && (strcmp(sectInfo.segInfo.segName, segName) == 0) ) {
+ size = sectInfo.sectSize;
+ result = (void*)(sectInfo.sectAddr + getSlide());
+ }
+ });
+ return result;
+}
+
+
+bool MachOLoaded::intersectsRange(uintptr_t start, uintptr_t length) const
+{
+ __block bool result = false;
+ uintptr_t slide = getSlide();
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( (info.vmAddr+info.vmSize+slide >= start) && (info.vmAddr+slide < start+length) )
+ result = true;
+ });
+ return result;
+}
+
+const uint8_t* MachOLoaded::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 > (uint64_t)(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;
+}
+
+bool MachOLoaded::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]) const
+{
+ 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_SHA384 ) {
+ uint8_t digest[CC_SHA384_DIGEST_LENGTH];
+ CC_SHA384(cd, cdLength, digest);
+ // cd-hash of sigs that use SHA384 is the first 20 bytes of the SHA384 of the code digest
+ memcpy(cdHash, digest, 20);
+ return true;
+ }
+ else if ( (cd->hashType == CS_HASHTYPE_SHA256) || (cd->hashType == CS_HASHTYPE_SHA256_TRUNCATED) ) {
+ 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;
+}
+
+
+// Note, this has to match the kernel
+static const uint32_t hashPriorities[] = {
+ CS_HASHTYPE_SHA1,
+ CS_HASHTYPE_SHA256_TRUNCATED,
+ CS_HASHTYPE_SHA256,
+ CS_HASHTYPE_SHA384,
+};
+
+static unsigned int hash_rank(const CS_CodeDirectory *cd)
+{
+ uint32_t type = cd->hashType;
+ for (uint32_t n = 0; n < sizeof(hashPriorities) / sizeof(hashPriorities[0]); ++n) {
+ if (hashPriorities[n] == type)
+ return n + 1;
+ }
+
+ /* not supported */
+ return 0;
+}
+
+
+// Note, this has to match the kernel
+static const uint32_t hashPriorities_watchOS[] = {
+ CS_HASHTYPE_SHA1
+};
+
+static unsigned int hash_rank_watchOS(const CS_CodeDirectory *cd)
+{
+ uint32_t type = cd->hashType;
+ for (uint32_t n = 0; n < sizeof(hashPriorities_watchOS) / sizeof(hashPriorities_watchOS[0]); ++n) {
+ if (hashPriorities_watchOS[n] == type)
+ return n + 1;
+ }
+
+ /* not supported */
+ return 0;
+}
+
+const void* MachOLoaded::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen) const
+{
+ // 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;
+
+ // Note: The kernel currently always uses sha1 for watchOS, even if other hashes are available.
+ const bool isWatchOS = this->supportsPlatform(Platform::watchOS);
+ auto hashRankFn = isWatchOS ? &hash_rank_watchOS : &hash_rank;
+
+ // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
+ const CS_CodeDirectory* bestCd = nullptr;
+ for (uint32_t i=0; i < subBlobCount; ++i) {
+ if ( codeSuperBlob->index[i].type == htonl(CSSLOT_CODEDIRECTORY) ) {
+ // Ok, this is the regular code directory
+ } else if ( codeSuperBlob->index[i].type >= htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES) && codeSuperBlob->index[i].type <= htonl(CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) {
+ // Ok, this is the alternative code directory
+ } else {
+ continue;
+ }
+ uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset);
+ // verify offset is not out of range
+ if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) )
+ continue;
+ 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) )
+ continue;
+ if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) ) {
+ if ( !bestCd || (hashRankFn(cd) > hashRankFn(bestCd)) )
+ bestCd = cd;
+ }
+ }
+ return bestCd;
+}
+
+
+// Regular pointer which needs to fit in 51-bits of value.
+// C++ RTTI uses the top bit, so we'll allow the whole top-byte
+// and the signed-extended bottom 43-bits to be fit in to 51-bits.
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signExtend51(uint64_t value51)
+{
+ uint64_t top8Bits = value51 & 0x007F80000000000ULL;
+ uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
+ uint64_t newValue = (top8Bits << 13) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ return newValue;
+}
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainRebase::signExtendedTarget() const
+{
+ return signExtend51(this->target);
+}
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainBind::signExtendedAddend() const
+{
+ uint64_t addend19 = this->addend;
+ if ( addend19 & 0x40000 )
+ return addend19 | 0xFFFFFFFFFFFC0000ULL;
+ else
+ return addend19;
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::keyName(uint8_t keyBits)
+{
+ static const char* names[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ assert(keyBits < 4);
+ return names[keyBits];
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthRebase::keyName() const
+{
+ return ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthBind::keyName() const
+{
+ return ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signPointer(void* loc, uint64_t target) const
+{
+#if __has_feature(ptrauth_calls)
+ uint64_t discriminator = authBind.diversity;
+ if ( authBind.addrDiv )
+ discriminator = __builtin_ptrauth_blend_discriminator(loc, discriminator);
+ switch ( authBind.key ) {
+ case 0: // IA
+ return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 0, discriminator);
+ case 1: // IB
+ return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 1, discriminator);
+ case 2: // DA
+ return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 2, discriminator);
+ case 3: // DB
+ return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 3, discriminator);
+ }
+#endif
+ return target;
+}
+
+
+
+} // 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 MachOLoaded_h
+#define MachOLoaded_h
+
+#include <stdint.h>
+
+#include "MachOFile.h"
+
+
+class CacheBuilder;
+
+namespace dyld3 {
+
+
+// A mach-o mapped into memory with zero-fill expansion
+// Can be used in dyld at runtime or during closure building
+struct VIS_HIDDEN MachOLoaded : public MachOFile
+{
+ typedef const MachOLoaded* (^DependentToMachOLoaded)(const MachOLoaded* image, uint32_t depIndex);
+
+ // for dlsym()
+ bool hasExportedSymbol(const char* symbolName, DependentToMachOLoaded finder, void** result,
+ bool* resultPointsToInstructions) const;
+
+ // for DYLD_PRINT_SEGMENTS
+ const char* segmentName(uint32_t segIndex) const;
+
+ // used to see if main executable overlaps shared region
+ bool intersectsRange(uintptr_t start, uintptr_t length) const;
+
+ // for _dyld_get_image_slide()
+ intptr_t getSlide() const;
+
+ // quick check if image has been incorporated into the dyld cache
+ bool inDyldCache() const { return (this->flags & 0x80000000); }
+
+ // for dladdr()
+ bool findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const;
+
+ // for _dyld_find_unwind_sections()
+ const void* findSectionContent(const char* segName, const char* sectName, uint64_t& size) const;
+
+ // used at runtime to validate loaded image matches closure
+ bool cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]) const;
+
+ // used by DyldSharedCache to find closure
+ static const uint8_t* trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);
+
+ // used by cache builder during error handling in chain bind processing
+ const char* dependentDylibLoadPath(uint32_t depIndex) const;
+
+ // used by closure builder to find the offset and size of the trie.
+ bool hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const;
+
+
+ // For use with new rebase/bind scheme were each fixup location on disk contains info on what
+ // fix up it needs plus the offset to the next fixup.
+ union ChainedFixupPointerOnDisk
+ {
+ struct PlainRebase
+ {
+ uint64_t target : 51,
+ next : 11,
+ bind : 1, // 0
+ auth : 1; // 0
+ uint64_t signExtendedTarget() const;
+ };
+ struct PlainBind
+ {
+ uint64_t ordinal : 16,
+ zero : 16,
+ addend : 19,
+ next : 11,
+ bind : 1, // 1
+ auth : 1; // 0
+ uint64_t signExtendedAddend() const;
+ };
+ struct AuthRebase
+ {
+ uint64_t target : 32,
+ diversity : 16,
+ addrDiv : 1,
+ key : 2,
+ next : 11,
+ bind : 1, // 0
+ auth : 1; // 1
+ const char* keyName() const;
+ };
+ struct AuthBind
+ {
+ uint64_t ordinal : 16,
+ zero : 16,
+ diversity : 16,
+ addrDiv : 1,
+ key : 2,
+ next : 11,
+ bind : 1, // 1
+ auth : 1; // 1
+ const char* keyName() const;
+ };
+
+ uint64_t raw;
+ AuthRebase authRebase;
+ AuthBind authBind;
+ PlainRebase plainRebase;
+ PlainBind plainBind;
+
+ static const char* keyName(uint8_t keyBits);
+ static uint64_t signExtend51(uint64_t);
+ uint64_t signPointer(void* loc, uint64_t target) const;
+ };
+
+protected:
+ friend CacheBuilder;
+
+ struct FoundSymbol {
+ enum class Kind { headerOffset, absolute, resolverOffset };
+ Kind kind;
+ bool isThreadLocal;
+ bool isWeakDef;
+ const MachOLoaded* foundInDylib;
+ uint64_t value;
+ uint32_t resolverFuncOffset;
+ const char* foundSymbolName;
+ };
+
+ struct LayoutInfo {
+ uintptr_t slide;
+ uintptr_t textUnslidVMAddr;
+ uintptr_t linkeditUnslidVMAddr;
+ uint32_t linkeditFileOffset;
+ uint32_t linkeditFileSize;
+ uint32_t linkeditSegIndex;
+ };
+
+ 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;
+ };
+
+ bool findExportedSymbol(Diagnostics& diag, const char* symbolName, FoundSymbol& foundInfo, DependentToMachOLoaded finder) const;
+ 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;
+ 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;
+ uint32_t dependentDylibCount() const;
+ bool findClosestFunctionStart(uint64_t address, uint64_t* functionStartAddress) const;
+
+ const void* findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen) const;
+
+};
+
+} // namespace dyld3
+
+#endif /* MachOLoaded_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;
- }
- // 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
#include <mach/mach.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <limits.h>
#include <sys/errno.h>
#include <unistd.h>
namespace dyld3 {
+namespace closure {
#if BUILDING_LIBDYLD
PathOverrides gPathOverrides;
return NULL;
}
+
+void PathOverrides::setFallbackPathHandling(FallbackPathMode mode)
+{
+ _fallbackPathMode = mode;
+}
-#if DYLD_IN_PROCESS
-void PathOverrides::setEnvVars(const char* envp[])
+void PathOverrides::setEnvVars(const char* envp[], const MachOFile* mainExe, const char* mainExePath)
{
for (const char** p = envp; *p != NULL; p++) {
addEnvVar(*p);
}
+ if ( mainExe != nullptr )
+ setMainExecutable(mainExe, mainExePath);
}
-#else
-PathOverrides::PathOverrides(const std::vector<std::string>& env)
+void PathOverrides::setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath)
{
- for (const std::string& envVar : env) {
- addEnvVar(envVar.c_str());
- }
+ assert(mainExe != nullptr);
+ assert(mainExe->isMainExecutable());
+ // process any LC_DYLD_ENVIRONMENT load commands in main executable
+ mainExe->forDyldEnv(^(const char* envVar, bool& stop) {
+ addEnvVar(envVar);
+ });
}
-#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;
void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath)) const
{
- if ( _insertedDylibs == nullptr )
+ if ( _insertedDylibs != nullptr ) {
+ forEachInColonList(_insertedDylibs, ^(const char* path, bool &stop) {
+ handler(path);
+ });
+ }
+}
+
+void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const
+{
+ if ( value == nullptr )
return;
- for (const char** lp=_insertedDylibs; *lp != nullptr; ++lp)
- handler(*lp);
+ 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::forEachEnvVar(void (^handler)(const char* envVar)) const
+{
+ handleEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverrides, handler);
+ handleEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverrides, handler);
+ handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks, handler);
+ handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacks, handler);
+ handleEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs, handler);
+ handleEnvVar("DYLD_IMAGE_SUFFIX", _imageSuffix, handler);
+ handleEnvVar("DYLD_ROOT_PATH", _rootPath, handler);
+}
+
+const char* PathOverrides::addString(const char* str)
+{
+ if ( _pathPool == nullptr )
+ _pathPool = PathPool::allocate();
+ return _pathPool->add(str);
+}
+
+void PathOverrides::setString(const char*& var, const char* value)
+{
+ if ( var == nullptr ) {
+ var = addString(value);
+ return;
+ }
+ // string already in use, build new appended string
+ char tmp[strlen(var)+strlen(value)+2];
+ strcpy(tmp, var);
+ strcat(tmp, ":");
+ strcat(tmp, value);
+ var = addString(tmp);
}
void PathOverrides::addEnvVar(const char* keyEqualsValue)
{
+ // We have to make a copy of the env vars because the dyld
+ // semantics is that the env vars are only looked at once
+ // at launch (using setenv() at runtime does not change dyld behavior).
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);
+ if ( strncmp(keyEqualsValue, "DYLD_LIBRARY_PATH", 17) == 0 ) {
+ setString(_dylibPathOverrides, &keyEqualsValue[18]);
}
- else if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
- _frameworkPathOverrides = parseColonListIntoArray(value);
+ else if ( strncmp(keyEqualsValue, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
+ setString(_frameworkPathOverrides, &keyEqualsValue[20]);
}
- else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
- _frameworkPathFallbacks = parseColonListIntoArray(value);
+ else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
+ setString(_frameworkPathFallbacks, &keyEqualsValue[29]);
}
- else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
- _dylibPathFallbacks = parseColonListIntoArray(value);
+ else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
+ setString(_dylibPathFallbacks, &keyEqualsValue[27]);
}
- else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
- _insertedDylibs = parseColonListIntoArray(value);
+ else if ( strncmp(keyEqualsValue, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
+ setString(_insertedDylibs, &keyEqualsValue[22]);
}
- else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
- _imageSuffix = value;
+ else if ( strncmp(keyEqualsValue, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
+ setString(_imageSuffix, &keyEqualsValue[18]);
}
- else if ( strcmp(key, "DYLD_ROOT_PATH") == 0 ) {
- _rootPath = value;
+ else if ( strncmp(keyEqualsValue, "DYLD_ROOT_PATH", 14) == 0 ) {
+ setString(_rootPath, &keyEqualsValue[15]);
}
}
}
-void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path))
+void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path, bool& stop))
{
char buffer[strlen(list)+1];
const char* t = list;
+ bool stop = false;
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);
+ handler(buffer, stop);
+ if ( stop )
+ return;
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);
+ handler(t, stop);
}
void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
{
- bool stop = false;
+ __block bool stop = false;
if ( _dylibPathFallbacks != nullptr ) {
- for (const char** fp=_dylibPathFallbacks; *fp != nullptr; ++fp) {
- handler(*fp, stop);
- if ( stop )
- return;
- }
+ forEachInColonList(_dylibPathFallbacks, ^(const char* pth, bool& innerStop) {
+ handler(pth, innerStop);
+ if ( innerStop )
+ stop = true;
+ });
}
else {
switch ( platform ) {
case Platform::macOS:
- // "$HOME/lib"
- handler("/usr/local/lib", stop); // FIXME: not for restricted processes
- if ( !stop )
- handler("/usr/lib", stop);
+ switch ( _fallbackPathMode ) {
+ case FallbackPathMode::classic:
+ // "$HOME/lib"
+ handler("/usr/local/lib", stop);
+ if ( stop )
+ break;
+ // fall thru
+ case FallbackPathMode::restricted:
+ handler("/usr/lib", stop);
+ break;
+ case FallbackPathMode::none:
+ break;
+ }
break;
case Platform::iOS:
case Platform::watchOS:
case Platform::tvOS:
case Platform::bridgeOS:
case Platform::unknown:
- handler("/usr/local/lib", stop);
- if ( !stop )
+ if ( _fallbackPathMode != FallbackPathMode::none ) {
+ handler("/usr/local/lib", stop);
+ if ( stop )
+ break;
+ }
+ // fall into /usr/lib case
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::watchOS_simulator:
+ case Platform::tvOS_simulator:
+ if ( _fallbackPathMode != FallbackPathMode::none )
handler("/usr/lib", stop);
break;
}
void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
{
- bool stop = false;
+ __block bool stop = false;
if ( _frameworkPathFallbacks != nullptr ) {
- for (const char** fp=_frameworkPathFallbacks; *fp != nullptr; ++fp) {
- handler(*fp, stop);
- if ( stop )
- return;
- }
+ forEachInColonList(_frameworkPathFallbacks, ^(const char* pth, bool& innerStop) {
+ handler(pth, innerStop);
+ if ( innerStop )
+ stop = true;
+ });
}
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);
+ switch ( _fallbackPathMode ) {
+ case FallbackPathMode::classic:
+ // "$HOME/Library/Frameworks"
+ handler("/Library/Frameworks", stop);
+ if ( stop )
+ break;
+ // "/Network/Library/Frameworks"
+ // fall thru
+ case FallbackPathMode::restricted:
+ handler("/System/Library/Frameworks", stop);
+ break;
+ case FallbackPathMode::none:
+ break;
+ }
break;
case Platform::iOS:
case Platform::watchOS:
case Platform::tvOS:
case Platform::bridgeOS:
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::watchOS_simulator:
+ case Platform::tvOS_simulator:
case Platform::unknown:
- handler("/System/Library/Frameworks", stop);
+ if ( _fallbackPathMode != FallbackPathMode::none )
+ 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
+
+//
+// copy path and add suffix to result
+//
+// /path/foo.dylib _debug => /path/foo_debug.dylib
+// foo.dylib _debug => foo_debug.dylib
+// foo _debug => foo_debug
+// /path/bar _debug => /path/bar_debug
+// /path/bar.A.dylib _debug => /path/bar.A_debug.dylib
+//
+void PathOverrides::addSuffix(const char* path, const char* suffix, char* result) const
+{
+ strcpy(result, path);
+
+ // find last slash
+ char* start = strrchr(result, '/');
+ if ( start != NULL )
+ start++;
+ else
+ start = result;
+
+ // find last dot after last slash
+ char* dot = strrchr(start, '.');
+ if ( dot != NULL ) {
+ strcpy(dot, suffix);
+ strcat(&dot[strlen(suffix)], &path[dot-result]);
+ }
+ else {
+ strcat(result, suffix);
+ }
+}
+
+void PathOverrides::forEachImageSuffix(const char* path, bool isFallbackPath, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const
+{
+ if ( _imageSuffix == nullptr ) {
+ handler(path, isFallbackPath, stop);
+ }
+ else {
+ forEachInColonList(_imageSuffix, ^(const char* suffix, bool& innerStop) {
+ char npath[strlen(path)+strlen(suffix)+8];
+ addSuffix(path, suffix, npath);
+ handler(npath, isFallbackPath, innerStop);
+ if ( innerStop )
+ stop = true;
+ });
+ if ( !stop )
+ handler(path, isFallbackPath, stop);
+ }
+}
+
+void PathOverrides::forEachPathVariant(const char* initialPath, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop), Platform platform) const
{
-#if DYLD_IN_PROCESS
- Platform platform = MachOParser::currentPlatform();
-#endif
__block bool stop = false;
// check for overrides
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);
+ forEachInColonList(_frameworkPathOverrides, ^(const char* frDir, bool &innerStop) {
+ char npath[strlen(frDir)+frameworkPartialPathLen+8];
+ strcpy(npath, frDir);
strcat(npath, "/");
strcat(npath, frameworkPartialPath);
- handler(npath, stop);
- if ( stop )
- return;
- }
+ forEachImageSuffix(npath, false, innerStop, handler);
+ if ( innerStop )
+ stop = true;
+ });
}
}
else {
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;
- }
+ forEachInColonList(_dylibPathOverrides, ^(const char* libDir, bool &innerStop) {
+ char npath[strlen(libDir)+libraryLeafNameLen+8];
+ strcpy(npath, libDir);
+ strcat(npath, "/");
+ strcat(npath, libraryLeafName);
+ forEachImageSuffix(npath, false, innerStop, handler);
+ if ( innerStop )
+ stop = true;
+ });
}
}
+ if ( stop )
+ return;
// try original path
- handler(initialPath, stop);
+ forEachImageSuffix(initialPath, false, stop, handler);
if ( stop )
return;
if ( frameworkPartialPath != nullptr ) {
const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
// look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
+ bool usesDefaultFallbackPaths = (_frameworkPathFallbacks == nullptr);
forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
char npath[strlen(dir)+frameworkPartialPathLen+8];
strcpy(npath, dir);
strcat(npath, "/");
strcat(npath, frameworkPartialPath);
- handler(npath, innerStop);
+ forEachImageSuffix(npath, usesDefaultFallbackPaths, innerStop, handler);
if ( innerStop )
- stop = innerStop;
+ stop = true;
});
}
const char* libraryLeafName = getLibraryLeafName(initialPath);
const size_t libraryLeafNameLen = strlen(libraryLeafName);
// look at each DYLD_FALLBACK_LIBRARY_PATH directory
+ bool usesDefaultFallbackPaths = (_dylibPathFallbacks == nullptr);
forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
char libpath[strlen(dir)+libraryLeafNameLen+8];
strcpy(libpath, dir);
strcat(libpath, "/");
strcat(libpath, libraryLeafName);
- handler(libpath, innerStop);
+ forEachImageSuffix(libpath, usesDefaultFallbackPaths, innerStop, handler);
if ( innerStop )
- stop = innerStop;
+ stop = true;
});
}
}
return path;
}
+
+
+//////////////////////////// PathPool ////////////////////////////////////////
+
+
+PathPool* PathPool::allocate()
+{
+ vm_address_t addr;
+ ::vm_allocate(mach_task_self(), &addr, kAllocationSize, VM_FLAGS_ANYWHERE);
+ PathPool* p = (PathPool*)addr;
+ p->_next = nullptr;
+ p->_current = &(p->_buffer[0]);
+ p->_bytesFree = kAllocationSize - sizeof(PathPool);
+ return p;
+}
+
+void PathPool::deallocate(PathPool* pool) {
+ do {
+ PathPool* next = pool->_next;
+ ::vm_deallocate(mach_task_self(), (vm_address_t)pool, kAllocationSize);
+ pool = next;
+ } while (pool);
+}
+
+const char* PathPool::add(const char* path)
+{
+ size_t len = strlen(path) + 1;
+ if ( len < _bytesFree ) {
+ char* result = _current;
+ strcpy(_current, path);
+ _current += len;
+ _bytesFree -= len;
+ return result;
+ }
+ if ( _next == nullptr )
+ _next = allocate();
+ return _next->add(path);
+}
+
+void PathPool::forEachPath(void (^handler)(const char* path))
+{
+ for (const char* s = _buffer; s < _current; ++s) {
+ handler(s);
+ s += strlen(s);
+ }
+
+ if ( _next != nullptr )
+ _next->forEachPath(handler);
+}
+
+
+
+} // namespace closure
} // namespace dyld3
#include <stdint.h>
-#if !DYLD_IN_PROCESS
-#include <vector>
-#include <string>
-#endif
-
#include "Logging.h"
-#include "MachOParser.h"
+#include "MachOFile.h"
namespace dyld3 {
+namespace closure {
+
+
+class VIS_HIDDEN PathPool
+{
+public:
+ static PathPool* allocate();
+ static void deallocate(PathPool* pool);
+ const char* add(const char* path);
+ void forEachPath(void (^handler)(const char* path));
+
+private:
+ enum { kAllocationSize = 32*1024 };
+
+ PathPool* _next;
+ char* _current;
+ size_t _bytesFree;
+ char _buffer[];
+};
+
class VIS_HIDDEN PathOverrides
{
// libdyld is never unloaded
~PathOverrides();
#endif
+ enum class FallbackPathMode { classic, restricted, none };
-#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
+ void setFallbackPathHandling(FallbackPathMode mode);
+ void setEnvVars(const char* envp[], const dyld3::MachOFile* mainExe, const char* mainExePath);
+ void setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath);
+ void forEachPathVariant(const char* requestedPath, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop),
+ Platform plat=MachOFile::currentPlatform()) const;
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 setString(const char*& var, const char* value);
+ const char* addString(const char* str);
+ static void forEachInColonList(const char* list, void (^callback)(const char* path, bool& stop));
void addEnvVar(const char* keyEqualsValue);
const char* getFrameworkPartialPath(const char* path) const;
static const char* getLibraryLeafName(const char* path);
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;
+ void forEachImageSuffix(const char* path, bool isFallbackPath, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const;
+ void addSuffix(const char* path, const char* suffix, char* result) const;
+
+ PathPool* _pathPool = nullptr;
+ 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
+ FallbackPathMode _fallbackPathMode = FallbackPathMode::classic;
};
#if BUILDING_LIBDYLD
#endif
+} // namespace closure
} // namespace dyld3
#endif // __DYLD_PATH_OVERRIDES_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
#define ARCH_NAME "arm64e"
#define ARCH_CACHE_MAGIC "dyld_v1 arm64e"
#elif __arm64__
- #define ARCH_NAME "arm64"
- #define ARCH_CACHE_MAGIC "dyld_v1 arm64"
+ #if __LP64__
+ #define ARCH_NAME "arm64"
+ #define ARCH_CACHE_MAGIC "dyld_v1 arm64"
+ #else
+ #define ARCH_NAME "arm64_32"
+ #define ARCH_CACHE_MAGIC "dyld_v1arm64_32"
+ #endif
#endif
-static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo)
+static void rebaseChainV2(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;
}
}
+#if !__LP64__
+static void rebaseChainV4(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info4* 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 & 0xFFFF8000) == 0 ) {
+ // small positive non-pointer, use as-is
+ }
+ else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
+ // small negative non-pointer
+ value |= 0xC0000000;
+ }
+ else {
+ 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;
+ }
+}
+#endif
static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
{
struct stat enableStatBuf;
struct stat devCacheStatBuf;
struct stat optCacheStatBuf;
+ bool developmentDevice = dyld3::internalInstall();
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 )
+ if ( developmentDevice && ((enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists) )
strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
#endif
if ( cache->header.mappingOffset < 0xE0 )
return true;
- if ( cache->header.platform != (uint32_t)MachOParser::currentPlatform() )
+ if ( cache->header.platform != (uint32_t)MachOFile::currentPlatform() )
return false;
#if TARGET_IPHONE_SIMULATOR
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;
}
- if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x120) ) {
+ if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x138) ) {
results->errorMessage = "shared cache file mappings are invalid";
::close(fd);
return false;
return false;
}
if ( memcmp(mappedData, firstPage, sizeof(firstPage)) != 0 ) {
- results->errorMessage = "first page of shared cache not mmap()able";
+ results->errorMessage = "first page of mmap()ed shared cache not valid";
::close(fd);
return false;
}
return true;
}
+
#if !TARGET_IPHONE_SIMULATOR
+
+// update all __DATA pages with slide info
+static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo* results)
+{
+ uint64_t dataPagesStart = info.mappings[1].sfm_address;
+ const dyld_cache_slide_info* slideInfo = nullptr;
+ if ( info.slideInfoSize != 0 ) {
+ slideInfo = (dyld_cache_slide_info*)(info.slideInfoAddressUnslid + results->slide);
+ }
+ const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+ if ( slideInfoHeader != nullptr ) {
+ 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);
+ 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);
+ rebaseChainV2(page, pageStartOffset, results->slide, slideHeader);
+ done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+ ++chainIndex;
+ }
+ }
+ else {
+ uint32_t pageOffset = pageEntry * 4;
+ //dyld::log(" start pageOffset=0x%03X\n", pageOffset);
+ rebaseChainV2(page, pageOffset, results->slide, slideHeader);
+ }
+ }
+ }
+#if __LP64__
+ else if ( slideInfoHeader->version == 3 ) {
+ const dyld_cache_slide_info3* slideHeader = (dyld_cache_slide_info3*)slideInfo;
+ const uint32_t pageSize = slideHeader->page_size;
+ for (int i=0; i < slideHeader->page_starts_count; ++i) {
+ uint8_t* page = (uint8_t*)(dataPagesStart + (pageSize*i));
+ uint64_t delta = slideHeader->page_starts[i];
+ if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE )
+ continue;
+ delta = delta/sizeof(uint64_t); // initial offset is byte based
+ dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)page;
+ do {
+ loc += delta;
+ delta = loc->plain.offsetToNextPointer;
+ if ( loc->auth.authenticated ) {
+#if __has_feature(ptrauth_calls)
+ uint64_t target = info.sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide;
+ MachOLoaded::ChainedFixupPointerOnDisk ptr;
+ ptr.raw = *((uint64_t*)loc);
+ loc->raw = ptr.signPointer(loc, target);
+#else
+ results->errorMessage = "invalid pointer kind in cache file";
+ return false;
+#endif
+ }
+ else {
+ loc->raw = MachOLoaded::ChainedFixupPointerOnDisk::signExtend51(loc->plain.pointerValue) + results->slide;
+ }
+ } while (delta != 0);
+ }
+ }
+#else
+ else if ( slideInfoHeader->version == 4 ) {
+ const dyld_cache_slide_info4* slideHeader = (dyld_cache_slide_info4*)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);
+ 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_SLIDE4_PAGE_NO_REBASE )
+ continue;
+ if ( pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+ uint16_t chainIndex = (pageEntry & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+ bool done = false;
+ while ( !done ) {
+ uint16_t pInfo = page_extras[chainIndex];
+ uint16_t pageStartOffset = (pInfo & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
+ //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
+ rebaseChainV4(page, pageStartOffset, results->slide, slideHeader);
+ done = (pInfo & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
+ ++chainIndex;
+ }
+ }
+ else {
+ uint32_t pageOffset = pageEntry * 4;
+ //dyld::log(" start pageOffset=0x%03X\n", pageOffset);
+ rebaseChainV4(page, pageOffset, results->slide, slideHeader);
+ }
+ }
+ }
+#endif // LP64
+ else {
+ results->errorMessage = "invalid slide info in cache file";
+ return false;
+ }
+ }
+ return true;
+}
+
static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
{
uint64_t cacheBaseAddress;
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 ) {
#endif
// <rdar://problem/32031197> respect -disable_aslr boot-arg
- if ( dyld3::loader::bootArgsContains("-disable_aslr") )
+ if ( dyld3::bootArgsContains("-disable_aslr") )
slide = 0;
// update mappings
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 ( 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";
+ if ( results->errorMessage == nullptr )
+ results->errorMessage = "syscall to map cache into shared region failed";
return false;
}
}
return true;
}
-#endif
+#endif // TARGET_IPHONE_SIMULATOR
static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
{
// 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);
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 TARGET_IPHONE_SIMULATOR // simulator caches do not support sliding
+ return true;
+#else
+ bool success = rebaseDataPages(options.verbose, info, results);
if ( options.verbose ) {
dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
verboseSharedCacheMappings(info.mappings);
}
- return true;
+ return success;
+#endif
}
{
results->loadAddress = 0;
results->slide = 0;
- results->cachedDylibsGroup = nullptr;
results->errorMessage = nullptr;
#if TARGET_IPHONE_SIMULATOR
}
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);
+ bool hasError = false;
+ if ( reuseExistingCache(options, results) ) {
+ hasError = (results->errorMessage != nullptr);
+ } else {
+ // slow path: this is first process to load cache
+ hasError = mapCacheSystemWide(options, results);
+ }
+ return hasError;
}
#endif
}
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) ) {
+ if ( loadInfo.loadAddress->header.formatVersion != dyld3::closure::kFormatVersion ) {
+ // support for older cache with a different Image* format
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ uint64_t hash = 0;
+ for (const char* s=dylibPathToFind; *s != '\0'; ++s)
+ hash += hash*4 + *s;
+#endif
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) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // on iOS, inode is used to hold hash of path
+ if ( (p->modTime == 0) && (p->inode != hash) )
+ continue;
+#endif
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;
+ results->image = 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);
+ const dyld3::closure::ImageArray* images = loadInfo.loadAddress->cachedDylibsImageArray();
+ results->image = nullptr;
+ uint32_t imageIndex;
+ if ( loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex) ) {
+ results->image = images->imageForNum(imageIndex+1);
+ }
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ else {
+ // <rdar://problem/32740215> handle symlink to cached dylib
+ if ( loadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
+ struct stat statBuf;
+ if ( dyld::my_stat(dylibPathToFind, &statBuf) == 0 ) {
+ // on macOS we store the inode and mtime of each dylib in the cache in the dyld_cache_image_info array
+ 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) {
+ if ( (p->inode == statBuf.st_ino) && (p->modTime == statBuf.st_mtime) ) {
+ imageIndex = (uint32_t)(p - start);
+ results->image = images->imageForNum(imageIndex+1);
+ break;
+ }
+ }
+ }
+ }
+ else {
+ char resolvedPath[PATH_MAX];
+ if ( realpath(dylibPathToFind, resolvedPath) != nullptr ) {
+ if ( loadInfo.loadAddress->hasImagePath(resolvedPath, imageIndex) ) {
+ results->image = images->imageForNum(imageIndex+1);
+ }
+ }
+ }
}
#endif
- if ( imageData == nullptr )
+ if ( results->image == nullptr )
return false;
- launch_cache::Image image(imageData);
- results->mhInCache = (const mach_header*)((uintptr_t)loadInfo.loadAddress + image.cacheOffset());
- results->pathInCache = image.path();
+ results->mhInCache = (const mach_header*)((uintptr_t)loadInfo.loadAddress + results->image->cacheOffset());
+ results->pathInCache = results->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) )
+ if ( (loadInfo.loadAddress == nullptr) || (loadInfo.loadAddress->header.formatVersion != closure::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);
+ uint32_t imageIndex;
+ return loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex);
}
};
struct SharedCacheLoadInfo {
- const DyldSharedCache* loadAddress;
- long slide;
- const launch_cache::binary_format::ImageGroup* cachedDylibsGroup;
- const char* errorMessage;
- char path[256];
+ const DyldSharedCache* loadAddress;
+ long slide;
+ 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;
+ const mach_header* mhInCache;
+ const char* pathInCache;
+ long slideInCache;
+ const closure::Image* image;
};
-
bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results);
bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind);
--- /dev/null
+/*
+ * Copyright (c) 2013 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 __START_GLUE_H__
+#define __START_GLUE_H__
+
+// Implemented in start_glue.s
+// Declare 'start' as a character, so that we can index into it.
+// Avoid arithmetic on function pointers.
+extern "C" char start;
+
+
+// <rdar://problem/12792039> need 'start' to be one atom, but entry is in interior
+
+#if __x86_64__ || __i386__
+ #define address_of_start (void*)((uintptr_t)&start + 1)
+#elif __arm64__
+ #define address_of_start (void*)((uintptr_t)&start + 4)
+#elif __arm__
+ #define address_of_start (void*)((uintptr_t)&start + 2)
+#endif
+
+
+
+#endif // __START_GLUE_H__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _DYLD_SUPPORTED_ARCHS_H_
+#define _DYLD_SUPPORTED_ARCHS_H_
+
+
+
+#endif // _DYLD_SUPPORTED_ARCHS_H_
#endif /* __LP64__ */
}
+// FIXME
+// We get distinct copies of this in libdyld and dyld. Eventually we can fix it,
+// for now we will just offset the values.
+
+#if BUILDING_DYLD
+static std::atomic<uint64_t> trace_pair_id(0);
+#else
+static std::atomic<uint64_t> trace_pair_id(1LL<<63);
+#endif
+
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);
- }
+bool kdebug_trace_dyld_enabled(uint32_t code) {
+ return kdebug_is_enabled(code);
}
-static std::atomic<uint64_t> trace_pair_id(0);
+VIS_HIDDEN
+void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4) {
+ if (kdebug_is_enabled(code)) {
+ data1.prepare(code);
+ data2.prepare(code);
+ data3.prepare(code);
+ data4.prepare(code);
+ kdebug_trace(code, data1.value(), data2.value(), data3.value(), data4.value());
+ data4.destroy(code);
+ data3.destroy(code);
+ data2.destroy(code);
+ data1.destroy(code);
+ }
+}
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();
+uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
+ uint64_t result = 0;
+ if (kdebug_is_enabled(code)) {
+ result = ++trace_pair_id;
+ data1.prepare(code);
+ data2.prepare(code);
+ data3.prepare(code);
+ kdebug_trace(code | DBG_FUNC_START, result, data1.value(), data2.value(), data3.value());
+ data3.destroy(code);
+ data2.destroy(code);
+ data1.destroy(code);
}
+ return result;
}
-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);
+VIS_HIDDEN
+void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
+ if (trace_id != 0 && kdebug_is_enabled(code)) {
+ data1.prepare(code);
+ data2.prepare(code);
+ data3.prepare(code);
+ kdebug_trace(code | DBG_FUNC_END, trace_id, data1.value(), data2.value(), data3.value());
+ data3.destroy(code);
+ data2.destroy(code);
+ data1.destroy(code);
}
}
+
+void ScopedTimer::startTimer() {
+ current_trace_id = kdebug_trace_dyld_duration_start(code, data1, data2, data3);
+}
+
+void ScopedTimer::endTimer() {
+ kdebug_trace_dyld_duration_end(current_trace_id, code, data4, data5, data6);
+}
+
};
#include <mach-o/loader.h>
#include <System/sys/kdebug.h>
-#ifndef DBG_DYLD_SIGNPOST
- #define DBG_DYLD_SIGNPOST (6)
-#endif
+#define DBG_DYLD_INTERNAL_SUBCLASS (7)
+#define DBG_DYLD_API_SUBCLASS (8)
+#define DBG_DYLD_DEBUGGING_SUBCLASS (9)
+
+#define DBG_DYLD_TIMING_STATIC_INITIALIZER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 0))
+#define DBG_DYLD_TIMING_LAUNCH_EXECUTABLE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 1))
+#define DBG_DYLD_TIMING_MAP_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 2))
+#define DBG_DYLD_TIMING_APPLY_FIXUPS (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 3))
+#define DBG_DYLD_TIMING_ATTACH_CODESIGNATURE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 4))
+#define DBG_DYLD_TIMING_BUILD_CLOSURE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 5))
+#define DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 6))
+#define DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 7))
+#define DBG_DYLD_TIMING_OBJC_INIT (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 8))
+#define DBG_DYLD_TIMING_OBJC_MAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 9))
+#define DBG_DYLD_TIMING_APPLY_INTERPOSING (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 10))
+#define DBG_DYLD_GDB_IMAGE_NOTIFIER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 11))
+#define DBG_DYLD_REMOTE_IMAGE_NOTIFIER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 12))
+
+#define DBG_DYLD_TIMING_DLOPEN (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 0))
+#define DBG_DYLD_TIMING_DLOPEN_PREFLIGHT (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 1))
+#define DBG_DYLD_TIMING_DLCLOSE (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 2))
+#define DBG_DYLD_TIMING_DLSYM (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 3))
+#define DBG_DYLD_TIMING_DLADDR (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 4))
+
+#define DBG_DYLD_DEBUGGING_VM_REMAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 0))
+#define DBG_DYLD_DEBUGGING_VM_UNMAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 1))
+#define DBG_DYLD_DEBUGGING_MAP_LOOP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 2))
+#define DBG_DYLD_DEBUGGING_MARK (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 3))
-#ifndef DBG_DYLD_TIMING
- #define DBG_DYLD_TIMING (7)
-#endif
-#ifndef DBG_DYLD_PRINT
- #define DBG_DYLD_PRINT (8)
-#endif
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
-#ifndef DBG_DYLD_SIGNPOST_START_DYLD
- #define DBG_DYLD_SIGNPOST_START_DYLD (0)
-#endif
+namespace dyld3 {
-#ifndef DBG_DYLD_SIGNPOST_START_MAIN
- #define DBG_DYLD_SIGNPOST_START_MAIN (1)
+struct VIS_HIDDEN kt_arg {
+ kt_arg(int value) : _value(value), _str(nullptr) {}
+ kt_arg(uint64_t value) : _value(value), _str(nullptr) {}
+ kt_arg(const char *value) : _value(0), _str(value) {}
+ kt_arg(void *value) : _value((uint64_t)value), _str(nullptr) {}
+ uint64_t value() const { return _value; }
+private:
+ void prepare(uint32_t code) {
+ if (_str) {
+ _value = kdebug_trace_string(code, 0, _str);
+ if (_value == (uint64_t)-1) _value = 0;
+ }
+ }
+ void destroy(uint32_t code) {
+ if (_str && _value) {
+ kdebug_trace_string(code, _value, nullptr);
+ }
+ }
+ friend class ScopedTimer;
+ friend uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
+ friend void kdebug_trace_dyld_duration_end(uint64_t pair_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
+ friend void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
+ uint64_t _value;
+ const char* _str;
+};
+
+class VIS_HIDDEN ScopedTimer {
+public:
+ ScopedTimer(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3)
+ : code(code), data1(data1), data2(data2), data3(data3), data4(0), data5(0), data6(0) {
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+ startTimer();
#endif
+ }
-#ifndef DBG_DYLD_SIGNPOST_START_MAIN_DYLD2
- #define DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 (2)
+ ~ScopedTimer() {
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+ endTimer();
#endif
-
-#ifndef DBG_DYLD_TIMING_STATIC_INITIALIZER
- #define DBG_DYLD_TIMING_STATIC_INITIALIZER (0)
+ }
+
+ void setData4(kt_arg data) { data4 = data; }
+ void setData5(kt_arg data) { data5 = data; }
+ void setData6(kt_arg data) { data6 = data; }
+private:
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+ void startTimer();
+ void endTimer();
#endif
-#ifndef DBG_DYLD_PRINT_GENERIC
- #define DBG_DYLD_PRINT_GENERIC (0)
-#endif
-
-
-#define VIS_HIDDEN __attribute__((visibility("hidden")))
-
-namespace dyld3 {
+ uint32_t code;
+ kt_arg data1;
+ kt_arg data2;
+ kt_arg data3;
+ kt_arg data4;
+ kt_arg data5;
+ kt_arg data6;
+ uint64_t current_trace_id = 0;
+};
VIS_HIDDEN
void kdebug_trace_dyld_image(const uint32_t code,
const mach_header* load_addr);
VIS_HIDDEN
-void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2);
+bool kdebug_trace_dyld_enabled(uint32_t code);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
VIS_HIDDEN
-void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)());
+uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
VIS_HIDDEN
-void kdebug_trace_print(const uint32_t code, const char *string);
-}
+void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
+};
#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 { };
-
-ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
-{
- return ClosureBuffer();
-}
-
-
-} // namespace dyld3
#include "dyld_priv.h"
#include "libdyldEntryVector.h"
#include "AllImages.h"
+#include "Array.h"
+#include "Loading.h"
#include "Logging.h"
#include "PathOverrides.h"
-#include "LaunchCacheFormat.h"
-#include "start_glue.h"
-
-extern "C" void start();
+#include "StartGlue.h"
+#include "dyld_process_info_internal.h"
+extern "C" char start;
VIS_HIDDEN const char** appleParams;
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)
+static void entry_setNotifyMonitoringDyldMain(void (*notifyMonitoringDyldMain)()) {
+ setNotifyMonitoringDyldMain(notifyMonitoringDyldMain);
+}
+
+static void entry_setNotifyMonitoringDyld(void (*notifyMonitoringDyld)(bool unloading,unsigned imageCount,
+ const struct mach_header* loadAddresses[],
+ const char* imagePaths[])) {
+ setNotifyMonitoringDyld(notifyMonitoringDyld);
+}
+
+static void entry_setInitialImageList(const closure::LaunchClosure* closure,
+ const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+ const Array<LoadedImage>& initialImages, const LoadedImage& libSystem)
{
gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages);
- gAllImages.applyInterposingToDyldCache(closure, initialImages);
+ gAllImages.applyInterposingToDyldCache(closure);
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);
+ gAllImages.runLibSystemInitializer(libSystem);
// now that malloc is available, parse DYLD_ env vars
- gPathOverrides.setEnvVars((const char**)environ);
+ closure::gPathOverrides.setEnvVars((const char**)environ, gAllImages.mainExecutable(), gAllImages.mainExecutableImage()->path());
}
static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress)
{
- gAllImages.runInitialzersBottomUp(mainExecutableImageLoadAddress);
+ gAllImages.runStartupInitialzers();
gAllImages.notifyMonitorMain();
}
sChildForkFunction = func;
}
-typedef void (*StartFunc)();
+static void entry_setRestrictions(bool allowAtPaths, bool allowEnvPaths)
+{
+ gAllImages.setRestrictions(allowAtPaths, allowEnvPaths);
+}
const LibDyldEntryVector entryVectorForDyld = {
LibDyldEntryVector::kCurrentVectorVersion,
- launch_cache::binary_format::kFormatVersion,
+ closure::kFormatVersion,
&entry_setVars,
&entry_setHaltFunction,
&entry_setOldAllImageInfo,
&entry_setInitialImageList,
&entry_runInitialzersBottomUp,
- (StartFunc)address_of_start,
+ (__typeof(LibDyldEntryVector::startFunc))address_of_start,
&entry_setChildForkFunction,
&entry_setLogFunction,
+ &entry_setRestrictions,
+ &entry_setNotifyMonitoringDyldMain,
+ &entry_setNotifyMonitoringDyld
};
VIS_HIDDEN void _dyld_fork_child()
#include <mach-o/loader.h>
-#include "LaunchCache.h"
#include "Loading.h"
struct dyld_all_image_infos;
+class DyldSharedCache;
namespace dyld3 {
+
struct LibDyldEntryVector
{
- enum { kCurrentVectorVersion = 4 };
+ enum { kCurrentVectorVersion = 6 };
uint32_t vectorVersion; // should be kCurrentVectorVersion
- uint32_t binaryFormatVersion; // should be launch_cache::binary_format::kFormatVersion
+ uint32_t binaryFormatVersion; // should be dyld3::closure::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 (*setInitialImageList)(const closure::LaunchClosure* closure,
+ const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+ const Array<LoadedImage>& initialImages, const LoadedImage& libSystem);
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));
+ // added in version 5
+ void (*setRestrictions)(bool allowAtPaths, bool allowEnvVars);
+ // added in version 6
+ void (*setNotifyMonitoringDyldMain)(void (*notifyMonitoringDyldMain)());
+ void (*setNotifyMonitoringDyld)(void (*notifyMonitoringDyldMain)(bool unloading, unsigned imageCount,
+ const struct mach_header* loadAddresses[],
+ const char* imagePaths[]));
};
extern const LibDyldEntryVector entryVectorForDyld;
#include "DyldSharedCache.h"
#include "Trie.hpp"
#include "MachOFileAbstraction.hpp"
+#include "MachOLoaded.h"
+#include "MachOAnalyzer.h"
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
class Adjustor {
public:
Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
- void adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR);
-
+ void adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
+ CacheBuilder::LOH_Tracker& lohTracker);
+
private:
- void adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR);
+ void adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker& lohTracker);
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);
+ uint64_t imageStartAddress, uint64_t imageEndAddress,
+ CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
+ uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress);
+ void adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker);
+ void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker);
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 adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta);
void rebuildLinkEditAndLoadCommands();
uint64_t slideForOrigAddress(uint64_t addr);
macho_header<P>* _mh;
Diagnostics& _diagnostics;
const uint8_t* _linkeditBias = nullptr;
- int64_t _linkeditAdjust = 0;
unsigned _linkeditSegIndex = 0;
bool _maskPointers = false;
bool _splitSegInfoV2 = false;
segCmd = (macho_segment_command<P>*)cmd;
_segCmds.push_back(segCmd);
_segOrigStartAddresses.push_back(segCmd->vmaddr());
- _segSlides.push_back(_mappingInfo[segIndex].dstCacheAddress - segCmd->vmaddr());
+ _segSlides.push_back(_mappingInfo[segIndex].dstCacheUnslidAddress - segCmd->vmaddr());
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
- _linkeditAdjust = _mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff();
- _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust;
+ _linkeditBias = (uint8_t*)_mappingInfo[segIndex].dstSegment - segCmd->fileoff();
_linkeditSegIndex = segIndex;
}
++segIndex;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
- _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64);
+ _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64) || (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64_32);
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)
+void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
+ CacheBuilder::LOH_Tracker& lohTracker)
{
if ( _diagnostics.hasError() )
return;
if ( _splitSegInfoV2 ) {
- adjustReferencesUsingInfoV2(pointersForASLR);
+ adjustReferencesUsingInfoV2(aslrTracker, lohTracker);
}
else {
- adjustDataPointers(pointersForASLR);
+ adjustDataPointers(aslrTracker);
adjustCode();
}
if ( _diagnostics.hasError() )
if ( _diagnostics.hasError() )
return;
rebuildLinkEditAndLoadCommands();
+
+#if DEBUG
+ Diagnostics diag;
+ ((dyld3::MachOAnalyzer*)_mh)->validateDyldCacheDylib(diag, _installName);
+ if ( diag.hasError() ) {
+ fprintf(stderr, "%s\n", diag.errorMessage().c_str());
+ }
+#endif
}
template <typename P>
// 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 weakBindOffset = bindOffset + bindSize;
uint32_t weakBindSize = _dyldInfo->weak_bind_size();
- uint32_t exportOffset = weakBindOffset + weakBindSize;
+ uint32_t lazyBindOffset = weakBindOffset + weakBindSize;
+ uint32_t lazyBindSize = _dyldInfo->lazy_bind_size();
+ uint32_t exportOffset = lazyBindOffset + lazyBindSize;
uint32_t exportSize = (uint32_t)newTrieBytes.size();
uint32_t splitSegInfoOffset = exportOffset + exportSize;
uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0);
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 ( symbolStringsSize )
memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize);
- memcpy((uint8_t*)_cacheBuffer+linkeditStartOffset, newLinkeditBufer, newLinkEditSize);
- ::bzero((uint8_t*)_cacheBuffer+linkeditStartOffset+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
+ memcpy(_mappingInfo[_linkeditSegIndex].dstSegment, newLinkeditBufer, newLinkEditSize);
+ ::bzero(((uint8_t*)_mappingInfo[_linkeditSegIndex].dstSegment)+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
::free(newLinkeditBufer);
+ uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheFileOffset;
// 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>));
dyldInfo->set_lazy_bind_size(lazyBindSize);
dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0);
dyldInfo->set_export_size(exportSize);
- break;
+ break;
case LC_FUNCTION_STARTS:
functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
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);
+ segFileOffsetDelta = (int32_t)(_mappingInfo[segIndex].dstCacheFileOffset - segCmd->fileoff());
+ segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheUnslidAddress);
segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize);
- segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheOffset);
+ segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheFileOffset);
segCmd->set_filesize(_mappingInfo[segIndex].copySegmentSize);
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
segCmd->set_vmsize(linkeditBufferSize);
}
template <typename P>
-void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR)
+void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker)
{
- pint_t* mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _mappingInfo[segIndex].dstCacheOffset + segOffset);
+ pint_t* mappedAddrP = (pint_t*)((uint8_t*)_mappingInfo[segIndex].dstSegment + segOffset);
uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP;
pint_t valueP;
uint32_t value32;
case REBASE_TYPE_POINTER:
valueP = (pint_t)P::getP(*mappedAddrP);
P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP));
- pointersForASLR.push_back(mappedAddrP);
+ aslrTracker.add(mappedAddrP);
break;
case REBASE_TYPE_TEXT_ABSOLUTE32:
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)
+ CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
+ 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;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk chainPtr;
int64_t offsetAdjust;
int64_t delta;
switch ( kind ) {
break;
case DYLD_CACHE_ADJ_V2_POINTER_32:
mappedAddr32 = (uint32_t*)mappedAddr;
- if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) {
+ if ( toNewAddress != (uint64_t)(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);
+ aslrTracker.add(mappedAddr32);
break;
case DYLD_CACHE_ADJ_V2_POINTER_64:
mappedAddr64 = (uint64_t*)mappedAddr;
return;
}
E::set64(*mappedAddr64, toNewAddress);
- pointersForASLR.push_back(mappedAddr);
+ aslrTracker.add(mappedAddr64);
break;
- case DYLD_CACHE_ADJ_V2_DELTA_64:
+ case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
+ mappedAddr64 = (uint64_t*)mappedAddr;
+ chainPtr.raw = E::get64(*mappedAddr64);
+ // ignore binds, fix up rebases to have new targets
+ if ( chainPtr.authRebase.bind == 0 ) {
+ if ( chainPtr.authRebase.auth ) {
+ // auth pointer target is offset in dyld cache
+ chainPtr.authRebase.target += (((dyld3::MachOAnalyzer*)_mh)->preferredLoadAddress() + targetSlide - _cacheBuffer->header.sharedRegionStart);
+ }
+ else {
+ // plain pointer target is unslid address of target
+ chainPtr.plainRebase.target += targetSlide;
+ }
+ // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location
+ E::set64(*mappedAddr64, chainPtr.raw);
+ }
+ 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_ARM64_ADRP:
mappedAddr32 = (uint32_t*)mappedAddr;
+ if (lohTracker)
+ (*lohTracker)[toNewAddress].insert(mappedAddr);
instruction = P::E::get32(*mappedAddr32);
if ( (instruction & 0x9F000000) == 0x90000000 ) {
int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF));
break;
case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
mappedAddr32 = (uint32_t*)mappedAddr;
+ if (lohTracker)
+ (*lohTracker)[toNewAddress].insert(mappedAddr);
instruction = P::E::get32(*mappedAddr32);
offsetAdjust = (adjust & 0xFFF);
if ( offsetAdjust == 0 )
}
template <typename P>
-void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR)
+void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
+ CacheBuilder::LOH_Tracker& lohTracker)
{
static const bool log = false;
sectionNewAddress.reserve(16);
sectionMappedAddress.reserve(16);
// section index 0 refers to mach_header
- sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[0].dstCacheOffset);
+ sectionMappedAddress.push_back((uint8_t*)_mappingInfo[0].dstSegment);
sectionSlides.push_back(_segSlides[0]);
- sectionNewAddress.push_back(_mappingInfo[0].dstCacheAddress);
+ sectionNewAddress.push_back(_mappingInfo[0].dstCacheUnslidAddress);
// section 1 and later refer to real sections
unsigned sectionIndex = 0;
+ unsigned objcSelRefsSectionIndex = ~0U;
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());
+ sectionMappedAddress.push_back((uint8_t*)_mappingInfo[segmentIndex].dstSegment + sect->addr() - segCmd->vmaddr());
sectionSlides.push_back(_segSlides[segmentIndex]);
- sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheAddress + sect->addr() - segCmd->vmaddr());
+ sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheUnslidAddress + sect->addr() - segCmd->vmaddr());
if (log) {
fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
}
++sectionIndex;
+ if (!strcmp(sect->segname(), "__DATA") && !strcmp(sect->sectname(), "__objc_selrefs"))
+ objcSelRefsSectionIndex = sectionIndex;
}
}
uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
uint64_t toSectionSlide = sectionSlides[toSectionIndex];
uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
+ CacheBuilder::LOH_Tracker* lohTrackerPtr = (toSectionIndex == objcSelRefsSectionIndex) ? &lohTracker : nullptr;
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) {
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);
+ if ( kind > 13 ) {
+ _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _installName);
return;
}
uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
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 ( toSectionIndex != 255 ) {
+ adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress);
+ }
if ( _diagnostics.hasError() )
return;
}
}
template <typename P>
-void Adjustor<P>::adjustDataPointers(std::vector<void*>& pointersForASLR)
+void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
{
const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()];
const uint8_t* end = &p[_dyldInfo->rebase_size()];
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
- slidePointer(segIndex, segOffset, type, pointersForASLR);
+ slidePointer(segIndex, segOffset, type, aslrTracker);
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);
+ slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- slidePointer(segIndex, segOffset, type, pointersForASLR);
+ slidePointer(segIndex, segOffset, type, aslrTracker);
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);
+ slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += skip + sizeof(pint_t);
}
break;
template <typename P>
-void Adjustor<P>::adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta)
+void Adjustor<P>::adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta)
{
- uint8_t* fixupLoc = (uint8_t*)_cacheBuffer + cacheOffset;
- uint32_t* fixupLoc32 = (uint32_t*)fixupLoc;
- uint64_t* fixupLoc64 = (uint64_t*)fixupLoc;
+ uint32_t* fixupLoc32 = (uint32_t*)textLoc;
+ uint64_t* fixupLoc64 = (uint64_t*)textLoc;
uint32_t instruction;
uint32_t value32;
uint64_t value64;
// 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;
+ uint8_t* textLoc = (uint8_t*)_mappingInfo[0].dstSegment;
while (uint64_t delta = read_uleb128(p, infoEnd)) {
- cacheOffset += delta;
- adjustInstruction(kind, cacheOffset, codeToDataDelta);
+ textLoc += delta;
+ adjustInstruction(kind, textLoc, codeToDataDelta);
}
}
}
} // anonymous namespace
-
-void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const
{
- if ( is64 ) {
- Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)mhInCache, mappingInfo, diag);
- adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ if ( _archLayout->is64 ) {
+ Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
+ adjustor64.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker);
}
else {
- Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)mhInCache, mappingInfo, diag);
- adjustor32.adjustImageForNewSegmentLocations(pointersForASLR);
+ Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
+ adjustor32.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker);
}
}
+
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);
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash,
+ bool emitDevCaches, bool isLocallyBuiltCache);
#endif /* BuilderUtils_h */
#include <set>
+#include <array>
#include <string>
#include <sstream>
#include <iomanip> // std::setfill, std::setw
}
bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose,
- bool skipWrites, bool agileChooseSHA256CdHash)
+ bool skipWrites, bool agileChooseSHA256CdHash, bool emitDevCaches, bool isLocallyBuiltCache)
{
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);
}
}
- manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, &buildQueue, &cacheSet, verbose](const std::string& arch) {
+ manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest,
+ &buildQueue, &cacheSet, skipWrites, verbose, emitDevCaches, isLocallyBuiltCache](const std::string& arch) {
std::string configPath;
std::string runtimePath = "/System/Library/Caches/com.apple.dyld/";
if (manifest.platform() == dyld3::Platform::macOS) {
configPath = masterDstRoot + runtimePath;
}
+ mkpath_np(configPath.c_str(), 0755);
if (manifest.platform() == dyld3::Platform::macOS) {
- buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch, verbose));
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch,
+ isLocallyBuiltCache, skipWrites, 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));
+ if (emitDevCaches)
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch,
+ isLocallyBuiltCache, skipWrites, verbose));
+ buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch,
+ isLocallyBuiltCache, skipWrites, verbose));
}
});
}
dispatch_sync(warningQueue, ^{
auto manifestWarnings = diags.warnings();
- warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
+ //warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
});
dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
for (const auto& configName : queueEntry.configNames) {
auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archName).results;
for (const auto& mh : results.evictions) {
- auto parser = dyld3::MachOParser(mh);
- configResults.exclude(&parser, "VM overflow, evicting");
+ configResults.exclude(mh, "VM overflow, evicting");
}
configResults.warnings = results.warnings;
if (queueEntry.options.optimizeStubs) {
- configResults.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+ configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
} else {
- configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+ configResults.developmentCache.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 {
+ cacheBuildFailure = true;
+ } else if (skipWrites) {
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);
}
});
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
+#include <mach/mach_vm.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach/shared_region.h>
#include <unordered_map>
#include <unordered_set>
-#include "MachOParser.h"
+#include "MachOFileAbstraction.hpp"
#include "CodeSigningTypes.h"
#include "DyldSharedCache.h"
#include "CacheBuilder.h"
#include "FileAbstraction.hpp"
-#include "LaunchCacheWriter.h"
#include "Trie.hpp"
+#include "FileUtils.h"
#include "Diagnostics.h"
-#include "ImageProxy.h"
+#include "ClosureBuilder.h"
+#include "Closure.h"
+#include "StringUtils.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
+ #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
+
+#ifndef ARM64_32_SHARED_REGION_START
+ #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
+ #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL
#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 }
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64", 0, 0, 0, 12, 2, true, true },
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64h", 0, 0, 0, 12, 2, true, true },
+ { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x00200000, 0x0, "i386", 0, 0, 0, 12, 0, false, false },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64", 0x0000C000, 0x00100000, 0x07F00000, 14, 2, false, true },
+#if SUPPORT_ARCH_arm64e
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64e", 0x0000C000, 0x00100000, 0x07F00000, 14, 2, false, true },
+#endif
+#if SUPPORT_ARCH_arm64_32
+ { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x02000000, 0xC0000000, "arm64_32",0x0000C000, 0x00100000, 0x07F00000, 14, 6, false, false },
+#endif
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x02000000, 0xE0000000, "armv7s", 0, 0, 0, 14, 4, false, false },
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x00400000, 0xE0000000, "armv7k", 0, 0, 0, 14, 4, false, false },
+ { 0x40000000, 0x40000000, 0x02000000, 0x0, "sim-x86", 0, 0, 0, 14, 0, false, false }
};
};
-CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options)
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
: _options(options)
- , _buffer(nullptr)
+ , _fileSystem(fileSystem)
+ , _fullAllocatedBuffer(0)
, _diagnostics(options.loggingPrefix, options.verbose)
, _archLayout(nullptr)
, _aliasCount(0)
, _slideInfoFileOffset(0)
, _slideInfoBufferSizeAllocated(0)
, _allocatedBufferSize(0)
- , _currentFileSize(0)
- , _vmSize(0)
, _branchPoolsLinkEditStartAddr(0)
{
break;
}
}
+
+ if (!_archLayout) {
+ _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
+ }
}
return _diagnostics.warnings();
}
-const std::set<const mach_header*> CacheBuilder::evictions()
+const std::set<const dyld3::MachOAnalyzer*> CacheBuilder::evictions()
{
return _evictions;
}
void CacheBuilder::deleteBuffer()
{
- vm_deallocate(mach_task_self(), (vm_address_t)_buffer, _allocatedBufferSize);
- _buffer = nullptr;
+ vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
+ _fullAllocatedBuffer = 0;
_allocatedBufferSize = 0;
}
-std::vector<DyldSharedCache::MappedMachO>
-CacheBuilder::makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+
+void CacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
{
- std::vector<DyldSharedCache::MappedMachO> sortedDylibs = dylibs;
+ for (const LoadedMachO& dylib : dylibs) {
+ _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
+ }
- 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);
+ std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
+ const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
+ const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
bool foundA = (orderA != sortOrder.end());
bool foundB = (orderB != sortOrder.end());
else if ( foundB )
return false;
else
- return a.runtimePath < b.runtimePath;
+ return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
});
-
- return sortedDylibs;
}
struct DylibAndSize
{
- const char* installName;
- uint64_t size;
+ const CacheBuilder::LoadedMachO* input;
+ const char* installName;
+ uint64_t size;
};
-bool CacheBuilder::cacheOverflow(const dyld_cache_mapping_info regions[3])
+uint64_t CacheBuilder::cacheOverflowAmount()
{
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) );
+ if ( _readExecuteRegion.sizeInUse > 0x60000000 )
+ return (_readExecuteRegion.sizeInUse - 0x60000000);
+
+ if ( _readWriteRegion.sizeInUse > 0x40000000 )
+ return (_readWriteRegion.sizeInUse - 0x40000000);
+
+ if ( _readOnlyRegion.sizeInUse > 0x3FE00000 )
+ return (_readOnlyRegion.sizeInUse - 0x3FE00000);
}
else {
- return (_vmSize > _archLayout->sharedMemorySize);
+ bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
+ uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
+ if ( alreadyOptimized )
+ vmSize += _readOnlyRegion.sizeInUse;
+ else if ( _options.excludeLocalSymbols )
+ vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %25 of original size
+ else
+ vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
+ if ( vmSize > _archLayout->sharedMemorySize )
+ return vmSize - _archLayout->sharedMemorySize;
}
+ // fits in shared region
+ return 0;
}
-void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
- const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
- const std::vector<DyldSharedCache::MappedMachO>& osExecutables)
+size_t CacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
{
- // <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;
+ // build count of how many references there are to each dylib
+ __block std::map<std::string, unsigned int> referenceCount;
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ referenceCount[loadPath] += 1;
+ });
}
- uint64_t t1 = mach_absolute_time();
+ // find all dylibs not referenced
+ std::vector<DylibAndSize> unreferencedDylibs;
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ if ( referenceCount.count(installName) == 0 ) {
+ // conservative: sum up all segments except LINKEDIT
+ __block uint64_t segsSize = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__LINKEDIT") != 0 )
+ segsSize += info.vmSize;
+ });
+ unreferencedDylibs.push_back({ dylib.input, installName, segsSize });
+ }
+ }
+ // sort leaf dylibs by size
+ std::sort(unreferencedDylibs.begin(), unreferencedDylibs.end(), [&](const DylibAndSize& a, const DylibAndSize& b) {
+ return ( a.size > b.size );
+ });
- // make copy of dylib list and sort
- std::vector<DyldSharedCache::MappedMachO> sortedDylibs = makeSortedDylibs(dylibs, _options.dylibOrdering);
- std::vector<DyldSharedCache::MappedMachO> otherOsDylibs = otherOsDylibsInput;
+ // build set of dylibs that if removed will allow cache to build
+ for (DylibAndSize& dylib : unreferencedDylibs) {
+ if ( _options.verbose )
+ _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+ _evictions.insert(dylib.input->mappedFile.mh);
+ // Track the evicted dylibs so we can try build "other" dlopen closures for them.
+ overflowDylibs.push_back(dylib.input);
+ if ( dylib.size > reductionTarget )
+ break;
+ reductionTarget -= dylib.size;
+ }
- // assign addresses for each segment of each dylib in new cache
- dyld_cache_mapping_info regions[3];
- SegmentMapping segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
- while ( cacheOverflow(regions) ) {
- if ( !_options.evictLeafDylibsOnOverflow ) {
- _diagnostics.error("cache overflow: %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
- return;
+ // prune _sortedDylibs
+ _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
+ return (_evictions.count(dylib.input->mappedFile.mh) != 0);
+ }),_sortedDylibs.end());
+
+ return _evictions.size();
+}
+
+// Handles building a list of input files to the CacheBuilder itself.
+class CacheInputBuilder {
+public:
+ CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
+ std::string reqArchitecture, dyld3::Platform reqPlatform)
+ : fileSystem(fileSystem), reqArchitecture(reqArchitecture), reqPlatform(reqPlatform) { }
+
+ // Loads and maps any MachOs in the given list of files.
+ void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
+ std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+ std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+ std::vector<CacheBuilder::LoadedMachO>& executables,
+ std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
+
+ std::map<std::string, uint64_t> dylibInstallNameMap;
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchitecture.c_str(), reqPlatform);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ if (ma == nullptr) {
+ couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
+ continue;
+ }
+
+ DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
+ loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
+
+ // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
+ if (ma->isDylib()) {
+ std::string installName = ma->installName();
+
+ // Let the platform exclude the file before we do anything else.
+ if (platformExcludesInstallName(installName)) {
+ inputFile.diag.verbose("Platform excluded file\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+
+ if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
+ inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
+ })) {
+ // TODO: Add exclusion lists here?
+ // Probably not as we already applied the dylib exclusion list.
+ otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ continue;
+ }
+
+ // Otherwise see if we have another file with this install name
+ auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
+ if (iteratorAndInserted.second) {
+ // We inserted the dylib so we haven't seen another with this name.
+ if (installName[0] != '@' && installName != inputFile.path) {
+ inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
+ }
+
+ dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else {
+ // We didn't insert this one so we've seen it before.
+ CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
+ inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
+
+ // This is the "Good" one, overwrite
+ if (inputFile.path == installName) {
+ // Unload the old one
+ fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
+
+ // And replace with this one.
+ previousLoadedMachO.mappedFile = mappedFile;
+ previousLoadedMachO.loadedFileInfo = loadedFileInfo;
+ }
+ }
+ } else if (ma->isBundle()) {
+ // TODO: Add exclusion lists here?
+ otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else if (ma->isDynamicExecutable()) {
+ if (platformExcludesExecutablePath_macOS(inputFile.path)) {
+ inputFile.diag.verbose("Platform excluded file\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+ executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else {
+ inputFile.diag.verbose("Unsupported mach file type\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ }
}
- // 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;
+ }
+
+private:
+
+
+
+ static bool platformExcludesInstallName_macOS(const std::string& installName) {
+ return false;
+ }
+
+ static bool platformExcludesInstallName_iOS(const std::string& installName) {
+ if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib" )
+ return true;
+ if ( installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" )
+ return true;
+ return false;
+ }
+
+ static bool platformExcludesInstallName_tvOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ static bool platformExcludesInstallName_watchOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ static bool platformExcludesInstallName_bridgeOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ // Returns true if the current platform requires that this install name be excluded from the shared cache
+ // Note that this overrides any exclusion from anywhere else.
+ bool platformExcludesInstallName(const std::string& installName) {
+ switch (reqPlatform) {
+ case dyld3::Platform::unknown:
+ return false;
+ case dyld3::Platform::macOS:
+ return platformExcludesInstallName_macOS(installName);
+ case dyld3::Platform::iOS:
+ return platformExcludesInstallName_iOS(installName);
+ case dyld3::Platform::tvOS:
+ return platformExcludesInstallName_tvOS(installName);
+ case dyld3::Platform::watchOS:
+ return platformExcludesInstallName_watchOS(installName);
+ case dyld3::Platform::bridgeOS:
+ return platformExcludesInstallName_bridgeOS(installName);
+ case dyld3::Platform::iOSMac:
+ return false;
+ case dyld3::Platform::iOS_simulator:
+ return false;
+ case dyld3::Platform::tvOS_simulator:
+ return false;
+ case dyld3::Platform::watchOS_simulator:
+ return false;
+ }
+ }
+
+
+
+
+ static bool platformExcludesExecutablePath_macOS(const std::string& path) {
+ return false;
+ }
+
+ static bool platformExcludesExecutablePath_iOS(const std::string& path) {
+ //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
+ if (path == "/sbin/launchd"
+ || path == "/usr/local/sbin/launchd.debug"
+ || path == "/usr/local/sbin/launchd.development"
+ || path == "/usr/libexec/installd") {
+ return true;
+ }
+ return false;
+ }
+
+ static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ // Returns true if the current platform requires that this path be excluded from the shared cache
+ // Note that this overrides any exclusion from anywhere else.
+ bool platformExcludesExecutablePath(const std::string& path) {
+ switch (reqPlatform) {
+ case dyld3::Platform::unknown:
+ return false;
+ case dyld3::Platform::macOS:
+ return platformExcludesExecutablePath_macOS(path);
+ case dyld3::Platform::iOS:
+ return platformExcludesExecutablePath_iOS(path);
+ case dyld3::Platform::tvOS:
+ return platformExcludesExecutablePath_tvOS(path);
+ case dyld3::Platform::watchOS:
+ return platformExcludesExecutablePath_watchOS(path);
+ case dyld3::Platform::bridgeOS:
+ return platformExcludesExecutablePath_bridgeOS(path);
+ case dyld3::Platform::iOSMac:
+ return false;
+ case dyld3::Platform::iOS_simulator:
+ return false;
+ case dyld3::Platform::tvOS_simulator:
+ return false;
+ case dyld3::Platform::watchOS_simulator:
+ return false;
+ }
+ }
+
+ const dyld3::closure::FileSystem& fileSystem;
+ std::string reqArchitecture;
+ dyld3::Platform reqPlatform;
+};
+
+static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+ std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+ std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
+{
+ // build map of dylibs
+ __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
+ __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ if (const char* installName = dylib.mappedFile.mh->installName()) {
+ knownDylibs.insert({ installName, &dylib });
+ allDylibs.insert({ installName, &dylib });
+ }
+ }
+
+ for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
+ allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ if (const char* installName = dylib.mappedFile.mh->installName())
+ allDylibs.insert({ installName, &dylib });
+ }
+
+ for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
+ allDylibs.insert({ dylib.inputFile->path, &dylib });
+ }
+
+ // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+ __block std::map<std::string, std::set<std::string>> badDylibs;
+ __block bool doAgain = true;
+ while ( doAgain ) {
+ doAgain = false;
+ // scan dylib list making sure all dependents are in dylib list
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if (isWeak)
+ return;
+ if ( knownDylibs.count(loadPath) == 0 ) {
+ badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
+ knownDylibs.erase(dylib.mappedFile.runtimePath);
+ knownDylibs.erase(dylib.mappedFile.mh->installName());
+ doAgain = true;
+ }
+ });
+ }
+ }
+
+ // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
+ for (auto badDylibsIterator : badDylibs) {
+ const std::string& dylibRuntimePath = badDylibsIterator.first;
+ auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
+ if (requiredDylibIterator == allDylibs.end())
+ continue;
+ if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
+ continue;
+ // This dylib is required so mark all dependencies as requried too
+ __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
+ worklist.push_back(requiredDylibIterator->second);
+ while (!worklist.empty()) {
+ const CacheBuilder::LoadedMachO* dylib = worklist.back();
+ worklist.pop_back();
+ if (!dylib->mappedFile.mh)
+ continue;
+ dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if (isWeak)
+ return;
+ auto dylibIterator = allDylibs.find(loadPath);
+ if (dylibIterator != allDylibs.end()) {
+ if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
+ dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
+ worklist.push_back(dylibIterator->second);
+ }
+ }
});
}
+ }
- // 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;
+ // FIXME: Make this an option we can pass in
+ const bool evictLeafDylibs = true;
+ if (evictLeafDylibs) {
+ doAgain = true;
+ while ( doAgain ) {
+ doAgain = false;
+
+ // build count of how many references there are to each dylib
+ __block std::set<std::string> referencedDylibs;
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ referencedDylibs.insert(loadPath);
});
- unreferencedDylibs.push_back({installName, segsSize});
+ }
+
+ // find all dylibs not referenced
+ std::vector<DylibAndSize> unreferencedDylibs;
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ const char* installName = dylib.mappedFile.mh->installName();
+ if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
+ badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
+ doAgain = true;
+ }
}
}
- // 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;
+ // Move bad dylibs from dylibs to cache to other dylibs.
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ auto i = badDylibs.find(dylib.mappedFile.runtimePath);
+ if ( i != badDylibs.end()) {
+ otherDylibs.push_back(dylib);
+ for (const std::string& reason : i->second )
+ otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
+ }
+ }
+
+ const auto& badDylibsLambdaRef = badDylibs;
+ dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
+ if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
+ return true;
+ return false;
+ }), dylibsToCache.end());
+}
+
+// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
+// We should remove the other build() method, or make it private so that this can wrap it.
+void CacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
+ std::vector<DyldSharedCache::FileAlias>& aliases) {
+ // First filter down to files which are actually MachO's
+ CacheInputBuilder cacheInputBuilder(_fileSystem, _archLayout->archName, _options.platform);
+
+ std::vector<LoadedMachO> dylibsToCache;
+ std::vector<LoadedMachO> otherDylibs;
+ std::vector<LoadedMachO> executables;
+ std::vector<LoadedMachO> couldNotLoadFiles;
+ cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
+
+ verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
+
+ // Check for required binaries before we try to build the cache
+ if (!_diagnostics.hasError()) {
+ // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+ std::string errorString;
+ for (const LoadedMachO& dylib : otherDylibs) {
+ if (dylib.inputFile->mustBeIncluded()) {
+ // An error loading a required file must be propagated up to the top level diagnostic handler.
+ bool gotWarning = false;
+ for (const std::string& warning : dylib.inputFile->diag.warnings()) {
+ gotWarning = true;
+ std::string message = warning;
+ if (message.back() == '\n')
+ message.pop_back();
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
+ }
+ if (!gotWarning) {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
+ }
+ }
}
- // 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() ) {
- _evictions.insert(parser.header());
- otherOsDylibs.push_back(*it);
- sortedDylibs.erase(it);
- break;
+ for (const LoadedMachO& dylib : couldNotLoadFiles) {
+ if (dylib.inputFile->mustBeIncluded()) {
+ if (dylib.inputFile->diag.hasError()) {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
+ } else {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
+
}
}
}
- // re-layout cache
- segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
- if ( unreferencedDylibs.size() == 0 && 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;
+ if (!errorString.empty()) {
+ _diagnostics.error("%s", errorString.c_str());
}
}
- // 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 ) {
+ if (!_diagnostics.hasError())
+ build(dylibsToCache, otherDylibs, executables, aliases);
+
+ if (!_diagnostics.hasError()) {
+ // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+ std::string errorString;
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
+ // An error loading a required file must be propagated up to the top level diagnostic handler.
+ std::string message = inputFile.diag.errorMessage();
+ if (message.back() == '\n')
+ message.pop_back();
+ errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
+ }
+ }
+ if (!errorString.empty()) {
+ _diagnostics.error("%s", errorString.c_str());
+ }
+ }
+
+ // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ for (const std::string& warning : inputFile.diag.warnings())
+ _diagnostics.warning("%s", warning.c_str());
+ }
+
+ // Clean up the loaded files
+ for (LoadedMachO& loadedMachO : dylibsToCache)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+ for (LoadedMachO& loadedMachO : otherDylibs)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+ for (LoadedMachO& loadedMachO : executables)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+}
+
+void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases) {
+
+ std::vector<LoadedMachO> dylibsToCache;
+ std::vector<LoadedMachO> otherDylibs;
+ std::vector<LoadedMachO> executables;
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ build(dylibsToCache, otherDylibs, executables, aliases);
+}
+
+void CacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
+ const std::vector<LoadedMachO>& otherOsDylibsInput,
+ const std::vector<LoadedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+ // <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;
+ }
+ uint64_t t1 = mach_absolute_time();
+
+ // make copy of dylib list and sort
+ makeSortedDylibs(dylibs, _options.dylibOrdering);
+
+ // allocate space used by largest possible cache plus room for LINKEDITS before optimization
+ _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
+ if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
_diagnostics.error("could not allocate buffer");
return;
}
- _currentFileSize = _allocatedBufferSize;
- // write unoptimized cache
- writeCacheHeader(regions, sortedDylibs, segmentMapping);
- copyRawSegments(sortedDylibs, segmentMapping);
- adjustAllImagesForNewSegmentLocations(sortedDylibs, segmentMapping);
+ // assign addresses for each segment of each dylib in new cache
+ assignSegmentAddresses();
+ std::vector<const LoadedMachO*> overflowDylibs;
+ while ( cacheOverflowAmount() != 0 ) {
+ if ( !_options.evictLeafDylibsOnOverflow ) {
+ _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
+ return;
+ }
+ size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
+ // re-layout cache
+ for (DylibInfo& dylib : _sortedDylibs)
+ dylib.cacheLocation.clear();
+ assignSegmentAddresses();
+
+ _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
+ }
+ markPaddingInaccessible();
+
+ // copy all segments into cache
+ uint64_t t2 = mach_absolute_time();
+ writeCacheHeader();
+ copyRawSegments();
+
+ // rebase all dylibs for new location in cache
+ uint64_t t3 = mach_absolute_time();
+ _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
+ adjustAllImagesForNewSegmentLocations();
if ( _diagnostics.hasError() )
return;
- bindAllImagesInCacheFile(regions);
+ // build ImageArray for dyld3, which has side effect of binding all cached dylibs
+ uint64_t t4 = mach_absolute_time();
+ buildImageArray(aliases);
if ( _diagnostics.hasError() )
return;
// optimize ObjC
+ uint64_t t5 = mach_absolute_time();
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
if ( _options.optimizeObjC )
- optimizeObjC(_buffer, _archLayout->is64, _options.optimizeStubs, _pointersForASLR, _diagnostics);
+ optimizeObjC();
if ( _diagnostics.hasError() )
return;
+
// optimize away stubs
+ uint64_t t6 = mach_absolute_time();
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) {
+ const uint64_t* p = (uint64_t*)((uint8_t*)dyldCache + dyldCache->header.branchPoolsOffset);
+ for (uint32_t i=0; i < dyldCache->header.branchPoolsCount; ++i) {
uint64_t poolAddr = p[i];
branchPoolStartAddrs.push_back(poolAddr);
branchPoolOffsets.push_back(poolAddr - cacheStartAddress);
}
- bypassStubs(_buffer, branchPoolStartAddrs, _s_neverStubEliminate, _diagnostics);
+ optimizeAwayStubs(branchPoolStartAddrs, _branchPoolsLinkEditStartAddr);
}
- 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)
+
+ // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
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();
+ uint64_t t7 = mach_absolute_time();
+ optimizeLinkedit(branchPoolOffsets);
- // 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);
+ // copy ImageArray to end of read-only region
+ addImageArray();
if ( _diagnostics.hasError() )
return;
- addCachedDylibsImageGroup(dylibGroup);
- if ( _diagnostics.hasError() )
- return;
-
- 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);
+ // compute and add dlopen closures for all other dylibs
+ addOtherImageArray(otherOsDylibsInput, overflowDylibs);
if ( _diagnostics.hasError() )
return;
- addCachedOtherDylibsImageGroup(otherGroup);
- if ( _diagnostics.hasError() )
- return;
-
- 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);
+ // compute and add launch closures to end of read-only region
+ uint64_t t8 = mach_absolute_time();
+ addClosures(osExecutables);
if ( _diagnostics.hasError() )
return;
- 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;
+ // update final readOnly region size
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
+ mappings[2].size = _readOnlyRegion.sizeInUse;
+ if ( _options.excludeLocalSymbols )
+ dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
- // record cache bounds
- _buffer->header.sharedRegionStart = _archLayout->sharedMemoryStart;
- _buffer->header.sharedRegionSize = _archLayout->sharedMemorySize;
+ // record max slide now that final size is established
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);
+ uint64_t maxSlide0 = 0x60000000 - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
+ uint64_t maxSlide1 = 0x40000000 - _readWriteRegion.sizeInUse;
+ uint64_t maxSlide2 = 0x3FE00000 - _readOnlyRegion.sizeInUse;
+ dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
}
else {
- _buffer->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (mappings[2].address + mappings[2].size);
+ dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
}
- // 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);
+ uint64_t t9 = 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 SUPPORT_ARCH_arm64e
+ if ( strcmp(_archLayout->archName, "arm64e") == 0 )
+ writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+ else
+#endif
+ if ( _archLayout->is64 )
+ writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+ else
+#if SUPPORT_ARCH_arm64_32
+ if ( strcmp(_archLayout->archName, "arm64_32") == 0 )
+ writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+ else
+#endif
+ writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
}
- 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;
+ uint64_t t10 = mach_absolute_time();
// 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);
+ if ( cacheOverflowAmount() != 0 ) {
+ _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
return;
}
if ( _diagnostics.hasError() )
return;
- uint64_t t8 = mach_absolute_time();
+ uint64_t t11 = 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;
+ fprintf(stderr, "time to layout cache: %ums\n", absolutetime_to_milliseconds(t2-t1));
+ fprintf(stderr, "time to copy cached dylibs into buffer: %ums\n", absolutetime_to_milliseconds(t3-t2));
+ fprintf(stderr, "time to adjust segments for new split locations: %ums\n", absolutetime_to_milliseconds(t4-t3));
+ fprintf(stderr, "time to bind all images: %ums\n", absolutetime_to_milliseconds(t5-t4));
+ fprintf(stderr, "time to optimize Objective-C: %ums\n", absolutetime_to_milliseconds(t6-t5));
+ fprintf(stderr, "time to do stub elimination: %ums\n", absolutetime_to_milliseconds(t7-t6));
+ fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t8-t7));
+ fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
+ fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
+ fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
}
return;
}
-void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& segmentMappings)
+void CacheBuilder::writeCacheHeader()
{
// "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
std::string magic = "dyld_v1";
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];
+ dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ memcpy(dyldCacheHeader->magic, magic.c_str(), 16);
+ dyldCacheHeader->mappingOffset = sizeof(dyld_cache_header);
+ dyldCacheHeader->mappingCount = 3;
+ dyldCacheHeader->imagesOffset = (uint32_t)(dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info) + sizeof(uint64_t)*_branchPoolStarts.size());
+ dyldCacheHeader->imagesCount = (uint32_t)_sortedDylibs.size() + _aliasCount;
+ dyldCacheHeader->dyldBaseAddress = 0;
+ dyldCacheHeader->codeSignatureOffset = 0;
+ dyldCacheHeader->codeSignatureSize = 0;
+ dyldCacheHeader->slideInfoOffset = _slideInfoFileOffset;
+ dyldCacheHeader->slideInfoSize = _slideInfoBufferSizeAllocated;
+ dyldCacheHeader->localSymbolsOffset = 0;
+ dyldCacheHeader->localSymbolsSize = 0;
+ dyldCacheHeader->cacheType = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+ dyldCacheHeader->accelerateInfoAddr = 0;
+ dyldCacheHeader->accelerateInfoSize = 0;
+ bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
+ dyldCacheHeader->branchPoolsOffset = dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info);
+ dyldCacheHeader->branchPoolsCount = (uint32_t)_branchPoolStarts.size();
+ dyldCacheHeader->imagesTextOffset = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
+ dyldCacheHeader->imagesTextCount = _sortedDylibs.size();
+ dyldCacheHeader->dylibsImageGroupAddr = 0;
+ dyldCacheHeader->dylibsImageGroupSize = 0;
+ dyldCacheHeader->otherImageGroupAddr = 0;
+ dyldCacheHeader->otherImageGroupSize = 0;
+ dyldCacheHeader->progClosuresAddr = 0;
+ dyldCacheHeader->progClosuresSize = 0;
+ dyldCacheHeader->progClosuresTrieAddr = 0;
+ dyldCacheHeader->progClosuresTrieSize = 0;
+ dyldCacheHeader->platform = (uint8_t)_options.platform;
+ dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
+ dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+ dyldCacheHeader->simulator = _options.forSimulator;
+ dyldCacheHeader->locallyBuiltCache = _options.isLocallyBuiltCache;
+ dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
+ dyldCacheHeader->sharedRegionStart = _archLayout->sharedMemoryStart;
+ dyldCacheHeader->sharedRegionSize = _archLayout->sharedMemorySize;
+
+ // fill in mappings
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
+ mappings[0].address = _readExecuteRegion.unslidLoadAddress;
+ mappings[0].fileOffset = 0;
+ mappings[0].size = _readExecuteRegion.sizeInUse;
+ mappings[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ mappings[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ mappings[1].address = _readWriteRegion.unslidLoadAddress;
+ mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
+ mappings[1].size = _readWriteRegion.sizeInUse;
+ mappings[1].maxProt = VM_PROT_READ | VM_PROT_WRITE;
+ mappings[1].initProt = VM_PROT_READ | VM_PROT_WRITE;
+ mappings[2].address = _readOnlyRegion.unslidLoadAddress;
+ mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
+ mappings[2].size = _readOnlyRegion.sizeInUse;
+ mappings[2].maxProt = VM_PROT_READ;
+ mappings[2].initProt = VM_PROT_READ;
// fill in branch pool addresses
- uint64_t* p = (uint64_t*)((char*)_buffer + _buffer->header.branchPoolsOffset);
+ uint64_t* p = (uint64_t*)(_readExecuteRegion.buffer + dyldCacheHeader->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;
+ dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
if ( _options.dylibsRemovedDuringMastering ) {
images->modTime = 0;
images->inode = pathHash(installName);
}
else {
- images->modTime = dylib.modTime;
- images->inode = dylib.inode;
+ images->modTime = dylib.input->mappedFile.modTime;
+ images->inode = dylib.input->mappedFile.inode;
}
- uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.mh);
- images->pathFileOffset = (uint32_t)segs[0].dstCacheOffset + installNameOffsetInTEXT;
+ uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
+ images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
++images;
}
// append aliases image records and strings
}
*/
// 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());
+ dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
+ uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.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;
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ dylib.input->mappedFile.mh->getUuid(textImages->uuid);
+ textImages->loadAddress = dylib.cacheLocation[0].dstCacheUnslidAddress;
+ textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
textImages->pathOffset = stringOffset;
- const char* installName = parser.installName();
- ::strcpy((char*)_buffer + stringOffset, installName);
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ ::strcpy((char*)_readExecuteRegion.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);
+ const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
assert(stringOffset <= (firstImage->address - mappings[0].address));
}
-
-void CacheBuilder::copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+void CacheBuilder::copyRawSegments()
{
- 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);
+ const bool log = false;
+ dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const DylibInfo& dylib = _sortedDylibs[index];
+ for (const SegmentMappingInfo& info : dylib.cacheLocation) {
+ if (log) 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, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
+ ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
+ if (uint64_t paddingSize = info.dstCacheSegmentSize - info.copySegmentSize) {
+ ::memset((char*)info.dstSegment + info.copySegmentSize, 0, paddingSize);
+ }
}
- }
-}
-
-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])
+void CacheBuilder::adjustAllImagesForNewSegmentLocations()
{
- 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 std::vector<Diagnostics> diags;
+ diags.resize(_sortedDylibs.size());
- __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;
+ if (_options.platform == dyld3::Platform::macOS) {
+ dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const DylibInfo& dylib = _sortedDylibs[index];
+ adjustDylibSegments(dylib, diags[index]);
});
- 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());
+ } else {
+ // Note this has to be done in serial because the LOH Tracker isn't thread safe
+ for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
+ const DylibInfo& dylib = _sortedDylibs[index];
+ adjustDylibSegments(dylib, diags[index]);
}
- 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);
+ for (const Diagnostics& diag : diags) {
+ if ( diag.hasError() ) {
+ _diagnostics.error("%s", diag.errorMessage().c_str());
+ break;
}
- 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])
+void CacheBuilder::assignSegmentAddresses()
{
// 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 )
+ 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);
+ startOffset += sizeof(dyld_cache_image_info) * _sortedDylibs.size();
+ startOffset += sizeof(dyld_cache_image_text_info) * _sortedDylibs.size();
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ startOffset += (strlen(dylib.input->mappedFile.mh->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
-
+ _readExecuteRegion.buffer = (uint8_t*)_fullAllocatedBuffer;
+ _readExecuteRegion.bufferSize = 0;
+ _readExecuteRegion.sizeInUse = 0;
+ _readExecuteRegion.unslidLoadAddress = _archLayout->sharedMemoryStart;
+ _readExecuteRegion.cacheFileOffset = 0;
+ __block uint64_t addr = _readExecuteRegion.unslidLoadAddress + 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) )
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.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) ) {
+ if ( (_archLayout->branchPoolTextSize != 0) && ((addr + segInfo.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;
+ addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+ uint64_t offsetInRegion = addr - _readExecuteRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readExecuteRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)offsetInRegion;
+ loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.copySegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.dstCacheSegmentSize;
});
}
// align TEXT region end
uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
- regions[0].size = endTextAddress - regions[0].address;
+ _readExecuteRegion.bufferSize = endTextAddress - _readExecuteRegion.unslidLoadAddress;
+ _readExecuteRegion.sizeInUse = _readExecuteRegion.bufferSize;
// 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;
+ _readWriteRegion.buffer = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+ _readWriteRegion.bufferSize = 0;
+ _readWriteRegion.sizeInUse = 0;
+ _readWriteRegion.unslidLoadAddress = addr;
+ _readWriteRegion.cacheFileOffset = _readExecuteRegion.sizeInUse;
// 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) )
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
return;
- if ( strcmp(segName, "__DATA_CONST") != 0 )
+ if ( strcmp(segInfo.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;
+ addr = align(addr, segInfo.p2align);
+ size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+ uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readWriteRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)segInfo.sizeOfSections;
+ loc.copySegmentSize = (uint32_t)copySize;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.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) )
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
return;
- if ( strcmp(segName, "__DATA_CONST") == 0 )
+ if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 )
return;
- if ( strcmp(segName, "__DATA_DIRTY") == 0 )
+ if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 )
return;
if ( dataConstSegmentCount > 10 ) {
// Pack __DATA segments only if we also have __DATA_CONST segments
- addr = align(addr, p2align);
+ addr = align(addr, segInfo.p2align);
}
else {
// Keep __DATA segments 4K or more aligned
- addr = align(addr, std::max(p2align, (uint8_t)12));
+ addr = align(addr, std::max((int)segInfo.p2align, (int)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;
+ size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+ uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readWriteRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)segInfo.sizeOfSections;
+ loc.copySegmentSize = (uint32_t)copySize;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.dstCacheSegmentSize;
});
}
- // layout all __DATA_DIRTY segments, sorted
+ // layout all __DATA_DIRTY segments, sorted (FIXME)
+ const size_t dylibCount = _sortedDylibs.size();
+ uint32_t dirtyDataSortIndexes[dylibCount];
+ for (size_t i=0; i < dylibCount; ++i)
+ dirtyDataSortIndexes[i] = (uint32_t)i;
+ std::sort(&dirtyDataSortIndexes[0], &dirtyDataSortIndexes[dylibCount], [&](const uint32_t& a, const uint32_t& b) {
+ const auto& orderA = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[a].input->mappedFile.runtimePath);
+ const auto& orderB = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[b].input->mappedFile.runtimePath);
+ bool foundA = (orderA != _options.dirtyDataSegmentOrdering.end());
+ bool foundB = (orderB != _options.dirtyDataSegmentOrdering.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 _sortedDylibs[a].input->mappedFile.runtimePath < _sortedDylibs[b].input->mappedFile.runtimePath;
+ });
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) )
+ for (size_t i=0; i < dylibCount; ++i) {
+ DylibInfo& dylib = _sortedDylibs[dirtyDataSortIndexes[i]];
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
return;
- if ( strcmp(segName, "__DATA_DIRTY") != 0 )
+ if ( strcmp(segInfo.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;
+ addr = align(addr, segInfo.p2align);
+ size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+ uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readWriteRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)segInfo.sizeOfSections;
+ loc.copySegmentSize = (uint32_t)copySize;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.dstCacheSegmentSize;
});
}
// align DATA region end
uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
- regions[1].size = endDataAddress - regions[1].address;
+ _readWriteRegion.bufferSize = endDataAddress - _readWriteRegion.unslidLoadAddress;
+ _readWriteRegion.sizeInUse = _readWriteRegion.bufferSize;
// 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;
+ _readOnlyRegion.buffer = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+ _readOnlyRegion.bufferSize = 0;
+ _readOnlyRegion.sizeInUse = 0;
+ _readOnlyRegion.unslidLoadAddress = addr;
+ _readOnlyRegion.cacheFileOffset = _readWriteRegion.cacheFileOffset + _readWriteRegion.sizeInUse;
// 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;
+ size_t slideInfoSize = sizeof(dyld_cache_slide_info);
+ slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info2));
+ slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info3));
+ slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info4));
+ _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage, _archLayout->sharedRegionAlignP2);
+ _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
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 )
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != VM_PROT_READ )
return;
- if ( strcmp(segName, "__LINKEDIT") == 0 )
+ if ( strcmp(segInfo.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;
+ addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+ uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readOnlyRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.copySegmentSize = (uint32_t)segInfo.sizeOfSections;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.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 )
+ // layout all LINKEDIT segments (after other read-only segments), aligned to 16KB
+ addr = align(addr, 14);
+ _nonLinkEditReadOnlySize = addr - _readOnlyRegion.unslidLoadAddress;
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != VM_PROT_READ )
return;
- if ( strcmp(segName, "__LINKEDIT") != 0 )
+ if ( strcmp(segInfo.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;
+ addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+ size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+ uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readOnlyRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.copySegmentSize = (uint32_t)copySize;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.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;
+ // align r/o region end
+ uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+ _readOnlyRegion.bufferSize = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
+ _readOnlyRegion.sizeInUse = _readOnlyRegion.bufferSize;
+
+ //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
+ //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", _readWriteRegion.buffer, _readWriteRegion.buffer+_readWriteRegion.bufferSize, _readWriteRegion.unslidLoadAddress);
+ //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer, _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
+
+ // sort SegmentMappingInfo for each image to be in the same order as original segments
+ for (DylibInfo& dylib : _sortedDylibs) {
+ std::sort(dylib.cacheLocation.begin(), dylib.cacheLocation.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+ return a.srcSegmentIndex < b.srcSegmentIndex;
+ });
+ }
+}
+
+void CacheBuilder::markPaddingInaccessible()
+{
+ // region between RX and RW
+ uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
+ uint8_t* endPad1 = _readWriteRegion.buffer;
+ ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0);
+
+ // region between RW and RO
+ uint8_t* startPad2 = _readWriteRegion.buffer+_readWriteRegion.sizeInUse;
+ uint8_t* endPad2 = _readOnlyRegion.buffer;
+ ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
+}
+
+
+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 = "???";
+ uint64_t unslidVmAddr = ((uint8_t*)contentPtr - _readExecuteRegion.buffer) + _readExecuteRegion.unslidLoadAddress;
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ ((dyld3::MachOLoaded*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+ if ( (unslidVmAddr >= info.vmAddr) && (unslidVmAddr < (info.vmAddr+info.vmSize)) ) {
+ foundDylibName = installName;
+ foundSegName = info.segName;
+ stop = true;
+ }
+ });
+ });
+}
+
+
+template <typename P>
+bool CacheBuilder::makeRebaseChainV2(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;
+ }
+ //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", 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 (unsigned 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::addPageStartsV2(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(uint32_t 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 ( !makeRebaseChainV2<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 v2 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(const bool bitmap[], unsigned dataPageCount)
+{
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+ const uint32_t pageSize = 4096;
- // 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;
- }
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)_readOnlyRegion.buffer;
+ 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
- // 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;
- });
+ // set page starts and extras for each page
+ std::vector<uint16_t> pageStarts;
+ std::vector<uint16_t> pageExtras;
+ pageStarts.reserve(dataPageCount);
+ uint8_t* pageContent = _readWriteRegion.buffer;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ //warning("page[%d]", i);
+ addPageStartsV2<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+ if ( _diagnostics.hasError() ) {
+ return;
+ }
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
}
- return result;
+ // 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
+ uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+ if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info overflow buffer");
+ }
+ ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+ //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
}
-uint64_t CacheBuilder::pathHash(const char* path)
+// fits in to int16_t
+static bool smallValue(uint64_t value)
{
- uint64_t sum = 0;
- for (const char* s=path; *s != '\0'; ++s)
- sum += sum*4 + *s;
- return sum;
+ uint32_t high = (value & 0xFFFF8000);
+ return (high == 0) || (high == 0xFFFF8000);
}
-
-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)
+bool CacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
{
typedef typename P::uint_t pint_t;
P::setP(*lastLoc, newLastValue);
return true;
}
- //warning(" too big delta = %d, lastOffset=0x%03X, offset=0x%03X", offset - lastLocationOffset, lastLocationOffset, offset);
+ //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
// distance between rebase locations is too far
// see if we can make a chain from non-rebase locations
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 ) {
+ if ( smallValue(value) ) {
// 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);
+ //fprintf(stderr, " no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
+ // lastLocationOffset, (long)lastValue, (long)newValue);
P::setP(*lastLoc, newValue);
return false;
}
// 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) {
+ for (unsigned 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);
+ if ( smallValue(value) )
+ newValue = (value & valueMask) | (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);
uint32_t delta3 = offset - prevOffset;
pint_t value = (pint_t)P::getP(*prevLoc);
pint_t newValue;
- if ( value == 0 )
- newValue = (delta3 << deltaShift);
+ if ( smallValue(value) )
+ newValue = (value & valueMask) | (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);
template <typename P>
-void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
+void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
{
typedef typename P::uint_t pint_t;
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 startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
uint16_t lastLocationOffset = 0xFFFF;
- for(int i=0; i < pageSize/4; ++i) {
+ for(uint32_t i=0; i < pageSize/4; ++i) {
unsigned offset = i*4;
if ( bitmap[i] ) {
- if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+ if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
// found first rebase location in page
startValue = i;
}
- else if ( !makeRebaseChain<P>(pageContent, lastLocationOffset, offset, info) ) {
+ else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
// can't record all rebasings in one chain
- if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
+ if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_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");
+ if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
+ _diagnostics.error("rebase overflow in v4 page extras");
return;
}
pageExtras.push_back(startValue);
- startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
+ startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
}
pageExtras.push_back(i);
}
pint_t newValue = ((lastValue - valueAdd) & valueMask);
P::setP(*lastLoc, newValue);
}
- if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+ if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
// add end bit to extras
- pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
+ pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
}
pageStarts.push_back(startValue);
}
+
+
template <typename P>
-void CacheBuilder::writeSlideInfoV2()
+void CacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
{
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;
+ dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)_readOnlyRegion.buffer;
+ info->version = 4;
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;;
+ pageStarts.reserve(dataPageCount);
+ uint8_t* pageContent = _readWriteRegion.buffer;
const bool* bitmapForPage = bitmap;
- for (unsigned i=0; i < pageCount; ++i) {
- //warning("page[%d]", i);
- addPageStarts<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ addPageStartsV4<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_offset = sizeof(dyld_cache_slide_info4);
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_offset = (unsigned)(sizeof(dyld_cache_slide_info4)+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 < 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");
+ uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+ if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info v4 overflow buffer");
}
- //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size());
+ ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+ //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
}
*/
-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;
+uint16_t CacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
+{
+ const int maxPerPage = pageSize / 4;
+ uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
+ for (int i=0; i < maxPerPage; ++i) {
+ if ( bitmap[i] ) {
+ if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
+ // found first rebase location in page
+ result = i * 4;
}
-
- if (illegalSectionSize) {
- _diagnostics.error("FIPS section %s/%s extends beyond the end of the segment", segName, sectionName);
- return;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
+ if ( lastLoc != nullptr ) {
+ // update chain (original chain may be wrong because of segment packing)
+ lastLoc->plainRebase.next = loc - lastLoc;
}
+ lastLoc = loc;
+ }
+ }
+ if ( lastLoc != nullptr ) {
+ // mark last one as end of chain
+ lastLoc->plainRebase.next = 0;
+ }
+ return result;
+}
- //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;
- });
+
+void CacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
+{
+ const uint32_t pageSize = 4096;
+
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
+ info->version = 3;
+ info->page_size = pageSize;
+ info->page_starts_count = dataPageCount;
+ info->auth_value_add = _archLayout->sharedMemoryStart;
+
+ // fill in per-page starts
+ uint8_t* pageContent = _readWriteRegion.buffer;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+
+ // update header with final size
+ dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
+ if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info overflow buffer");
+ }
+}
+
+
+void CacheBuilder::fipsSign()
+{
+ // find libcorecrypto.dylib in cache being built
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ __block const dyld3::MachOLoaded* ml = nullptr;
+ dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+ if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
+ ml = (dyld3::MachOLoaded*)mh;
});
+ if ( ml == nullptr ) {
+ _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
+ return;
+ }
+
+ // find location in libcorecrypto.dylib to store hash of __text section
+ uint64_t hashStoreSize;
+ const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+ if ( hashStoreLocation == nullptr ) {
+ _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
+ return;
+ }
+ if ( hashStoreSize != 32 ) {
+ _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
+ return;
+ }
- if (!found) {
- _diagnostics.warning("Could not find /usr/lib/system/libcorecrypto.dylib, skipping FIPS sealing");
+ // compute hmac hash of __text section
+ uint64_t textSize;
+ const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
+ if ( textLocation == nullptr ) {
+ _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
+ return;
}
+ unsigned char hmac_key = 0;
+ CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
}
void CacheBuilder::codeSign()
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;
+ size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
// layout code signature contents
uint32_t blobCount = agile ? 4 : 3;
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);
+ // allocate space for blob
+ vm_address_t codeSigAlloc;
+ if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
+ _diagnostics.error("could not allocate code signature buffer");
return;
}
+ _codeSignatureRegion.buffer = (uint8_t*)codeSigAlloc;
+ _codeSignatureRegion.bufferSize = sigSize;
+ _codeSignatureRegion.sizeInUse = sigSize;
// create overall code signature which is a superblob
- CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(csBuffer);
+ CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
sb->length = htonl(sbSize);
sb->count = htonl(blobCount);
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
+ cd->execSegBase = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
+ cd->execSegLimit = htonll(_readExecuteRegion.sizeInUse); // size of TEXT segment
+ cd->execSegFlags = 0; // not a main binary
// initialize dynamic fields of Code Directory
strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
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;
+ dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+ cache->codeSignatureOffset = inBbufferSize;
+ cache->codeSignatureSize = sigSize;
+
+ const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / CS_PAGE_SIZE);
+ const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / CS_PAGE_SIZE);
+ const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / CS_PAGE_SIZE);
+ auto codeSignPage = ^(size_t i) {
+ const uint8_t* code = nullptr;
+ // move to correct region
+ if ( i < rwSlotStart )
+ code = _readExecuteRegion.buffer + (i * CS_PAGE_SIZE);
+ else if ( i >= rwSlotStart && i < roSlotStart )
+ code = _readWriteRegion.buffer + ((i - rwSlotStart) * CS_PAGE_SIZE);
+ else if ( i >= roSlotStart && i < localsSlotStart )
+ code = _readOnlyRegion.buffer + ((i - roSlotStart) * CS_PAGE_SIZE);
+ else
+ code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * CS_PAGE_SIZE);
- // compute hashes
- const uint8_t* code = inBuffer;
- for (uint32_t i=0; i < slotCount; ++i) {
- CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot);
- hashSlot += dscHashSize;
+ CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot + (i * dscHashSize));
if ( agile ) {
- CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot);
- hash256Slot += CS_HASH_SIZE_SHA256;
+ CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot + (i * CS_HASH_SIZE_SHA256));
}
- code += CS_PAGE_SIZE;
+ };
+
+ // compute hashes
+ dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
+ codeSignPage(i);
+ });
+
+ // Now that we have a code signature, compute a UUID from it.
+
+ // Clear existing UUID, then MD5 whole cache buffer.
+ {
+ uint8_t* uuidLoc = cache->uuid;
+ assert(uuid_is_null(uuidLoc));
+ static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE == 0, "uuid is expected in the first page of the cache");
+ CC_MD5((const void*)cd, (unsigned)cdSize, 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;
+
+ // Now codesign page 0 again
+ codeSignPage(0);
}
// hash of entire code directory (cdHash) uses same hash as each page
else {
memset(_cdHashSecond, 0, 20);
}
-
- // increase file size to include newly append code signature
- _currentFileSize += sigSize;
}
const bool CacheBuilder::agileSignature()
return cdHash(_cdHashSecond);
}
-void CacheBuilder::addCachedDylibsImageGroup(dyld3::ImageProxyGroup* dylibGroup)
+
+void CacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
{
- const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = dylibGroup->makeImageGroupBinary(_diagnostics, _s_neverStubEliminate);
- if (!groupBinary)
+ typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo;
+ typedef dyld3::closure::Image::PatchableExport::PatchLocation PatchLocation;
+ typedef uint64_t CacheOffset;
+
+ // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
+ __block std::vector<CachedDylibInfo> dylibInfos;
+ __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ uint64_t mtime;
+ uint64_t inode;
+ cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
+ CachedDylibInfo entry;
+ entry.fileInfo.fileContent = mh;
+ entry.fileInfo.path = installName;
+ entry.fileInfo.sliceOffset = 0;
+ entry.fileInfo.inode = inode;
+ entry.fileInfo.mtime = mtime;
+ dylibInfos.push_back(entry);
+ imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
+ });
+
+ // Convert symlinks from STL to simple char pointers.
+ std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
+ dylibAliases.reserve(aliases.size());
+ for (const auto& alias : aliases)
+ dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
+
+
+ __block std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>> dylibToItsExports;
+ __block std::unordered_map<CacheOffset, std::vector<PatchLocation>> exportsToUses;
+ __block std::unordered_map<CacheOffset, const char*> exportsToName;
+
+ dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers;
+
+ handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress,
+ const dyld3::Array<uint64_t>& starts,
+ const dyld3::Array<dyld3::closure::Image::ResolvedSymbolTarget>& targets,
+ const dyld3::Array<dyld3::closure::ClosureBuilder::ResolvedTargetInfo>& targetInfos) {
+ for (uint64_t start : starts) {
+ dyld3::closure::Image::forEachChainedFixup((void*)imageLoadAddress, start, ^(uint64_t* fixupLoc, dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stop) {
+ // record location in aslr tracker so kernel can slide this on page-in
+ _aslrTracker.add(fixupLoc);
+
+ // if bind, record info for patch table and convert to rebase
+ if ( fixupInfo.plainBind.bind ) {
+ dyld3::closure::Image::ResolvedSymbolTarget target = targets[fixupInfo.plainBind.ordinal];
+ const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo = targetInfos[fixupInfo.plainBind.ordinal];
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc;
+ uint64_t offsetInCache;
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc;
+ offsetInCache = target.sharedCache.offset - targetInfo.addend;
+ dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
+ exportsToName[offsetInCache] = targetInfo.foundSymbolName;
+ if ( fixupInfo.authBind.auth ) {
+ // turn this auth bind into an auth rebase into the cache
+ loc->authRebase.bind = 0;
+ loc->authRebase.target = target.sharedCache.offset;
+ exportsToUses[offsetInCache].push_back(PatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo.addend, *loc));
+ }
+ else {
+ // turn this plain bind into an plain rebase into the cache
+ loc->plainRebase.bind = 0;
+ loc->plainRebase.target = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+ exportsToUses[offsetInCache].push_back(PatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
+ }
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ if ( _archLayout->is64 )
+ *((uint64_t*)fixupLoc) = target.absolute.value;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
+ // don't record absolute targets for ASLR
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ }
+ });
+ }
+ };
+
+ handlers.rebase = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* imageToFix, uint32_t runtimeOffset) {
+ // record location in aslr tracker so kernel can slide this on page-in
+ uint8_t* fixupLoc = (uint8_t*)imageToFix+runtimeOffset;
+ _aslrTracker.add(fixupLoc);
+ };
+
+ handlers.bind = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* mh,
+ uint32_t runtimeOffset, dyld3::closure::Image::ResolvedSymbolTarget target,
+ const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo) {
+ uint8_t* fixupLoc = (uint8_t*)mh+runtimeOffset;
+
+ // binder is called a second time for weak_bind info, which we ignore when building cache
+ const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc);
+
+ // do actual bind that sets pointer in image to unslid target address
+ uint64_t offsetInCache;
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ offsetInCache = target.sharedCache.offset - targetInfo.addend;
+ dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
+ exportsToUses[offsetInCache].push_back(PatchLocation(fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
+ exportsToName[offsetInCache] = targetInfo.foundSymbolName;
+ if ( !weakDefUseAlreadySet ) {
+ if ( _archLayout->is64 )
+ *((uint64_t*)fixupLoc) = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset);
+ // record location in aslr tracker so kernel can slide this on page-in
+ _aslrTracker.add(fixupLoc);
+ }
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ if ( _archLayout->is64 )
+ *((uint64_t*)fixupLoc) = target.absolute.value;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
+ // don't record absolute targets for ASLR
+ // HACK: Split seg may have added a target. Remove it
+ _aslrTracker.remove(fixupLoc);
+ if ( (targetInfo.libOrdinal > 0) && (targetInfo.libOrdinal <= mh->dependentDylibCount()) ) {
+ _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1);
+ }
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ };
+
+ handlers.forEachExportsPatch = ^(dyld3::closure::ImageNum imageNum, void (^handler)(const dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers::PatchInfo&)) {
+ const dyld3::MachOLoaded* ml = imageNumToML[imageNum];
+ for (CacheOffset exportCacheOffset : dylibToItsExports[ml]) {
+ dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers::PatchInfo info;
+ std::vector<PatchLocation>& uses = exportsToUses[exportCacheOffset];
+ uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
+ info.exportCacheOffset = (uint32_t)exportCacheOffset;
+ info.exportSymbolName = exportsToName[exportCacheOffset];
+ info.usesCount = (uint32_t)uses.size();
+ info.usesArray = &uses.front();
+ handler(info);
+ }
+ };
+
+
+ // build ImageArray for all dylibs in dyld cache
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::none, nullptr, _archLayout->archName, _options.platform, &handlers);
+ dyld3::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
+ const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
+ _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
+ if ( cb.diagnostics().hasError() ) {
+ _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
return;
+ }
+}
- dyld3::launch_cache::ImageGroup group(groupBinary);
- size_t groupSize = group.size();
+void CacheBuilder::addImageArray()
+{
+ // build trie of dylib paths
+ __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
+ _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
+ image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
+ dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
+ });
+ });
+ DylibIndexTrie dylibsTrie(dylibEntrys);
+ std::vector<uint8_t> trieBytes;
+ dylibsTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 4) != 0 )
+ trieBytes.push_back(0);
- 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);
+ // check for fit
+ uint64_t imageArraySize = _imageArray->size();
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ if ( imageArraySize+trieBytes.size() > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/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);
+ // copy into cache and update header
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ dyldCache->header.dylibsImageArraySize = imageArraySize;
+ dyldCache->header.dylibsTrieAddr = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
+ dyldCache->header.dylibsTrieSize = trieBytes.size();
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+ _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
}
-
-void CacheBuilder::addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup* otherGroup)
+void CacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
{
- const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = otherGroup->makeImageGroupBinary(_diagnostics);
- if (!groupBinary)
- return;
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, _fileSystem, cache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::none, nullptr, _archLayout->archName, _options.platform);
+
+ // make ImageArray for other dylibs and bundles
+ STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
+ for (const LoadedMachO& other : otherDylibsAndBundles) {
+ if ( !contains(other.loadedFileInfo.path, ".app/") )
+ others.push_back(other.loadedFileInfo);
+ }
+
+ for (const LoadedMachO* dylib : overflowDylibs) {
+ if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
+ others.push_back(dylib->loadedFileInfo);
+ }
- dyld3::launch_cache::ImageGroup group(groupBinary);
- size_t groupSize = group.size();
+ // Sort the others array by name so that it is deterministic
+ std::sort(others.begin(), others.end(),
+ [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
+ return strcmp(a.path, b.path) < 0;
+ });
- 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);
+ const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
+
+ // build trie of paths
+ __block std::vector<DylibIndexTrie::Entry> otherEntrys;
+ otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ if ( !image->isInvalid() )
+ otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+ });
+ DylibIndexTrie dylibsTrie(otherEntrys);
+ std::vector<uint8_t> trieBytes;
+ dylibsTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 4) != 0 )
+ trieBytes.push_back(0);
+
+ // check for fit
+ uint64_t imageArraySize = otherImageArray->size();
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ if ( imageArraySize+trieBytes.size() > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/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);
+ // copy into cache and update header
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ dyldCache->header.otherImageArraySize = imageArraySize;
+ dyldCache->header.otherTrieAddr = dyldCache->header.otherImageArrayAddr + imageArraySize;
+ dyldCache->header.otherTrieSize = trieBytes.size();
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+ _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
}
-void CacheBuilder::addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures)
+
+void CacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
{
+ const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+
+ __block std::vector<Diagnostics> osExecutablesDiags;
+ __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
+ osExecutablesDiags.resize(osExecutables.size());
+ osExecutablesClosures.resize(osExecutables.size());
+
+ dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const LoadedMachO& loadedMachO = osExecutables[index];
+ // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
+ if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
+ return;
+ }
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::all, nullptr, _archLayout->archName, _options.platform, nullptr);
+ bool issetuid = false;
+ if ( this->_options.platform == dyld3::Platform::macOS )
+ _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid);
+ const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
+ if ( builder.diagnostics().hasError() ) {
+ osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
+ }
+ else {
+ assert(mainClosure != nullptr);
+ osExecutablesClosures[index] = mainClosure;
+ }
+ });
+
+ std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
+ for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
+ const LoadedMachO& loadedMachO = osExecutables[i];
+ const Diagnostics& diag = osExecutablesDiags[i];
+ if (diag.hasError()) {
+ if ( _options.verbose ) {
+ _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
+ for (const std::string& warn : diag.warnings() )
+ _diagnostics.warning("%s", warn.c_str());
+ }
+ if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
+ loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
+ }
+ } else {
+ // Note, a closure could be null here if it has a path we skip.
+ if (osExecutablesClosures[i] != nullptr)
+ closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
+ }
+ }
+
+ osExecutablesDiags.clear();
+ osExecutablesClosures.clear();
+
// preflight space needed
size_t closuresSpace = 0;
for (const auto& entry : closures) {
- dyld3::launch_cache::Closure closure(entry.second);
- closuresSpace += closure.size();
+ closuresSpace += entry.second->size();
}
- size_t freeSpace = _allocatedBufferSize - _currentFileSize;
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
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;
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
std::vector<DylibIndexTrie::Entry> closureEntrys;
uint32_t currentClosureOffset = 0;
for (const auto& entry : closures) {
- const dyld3::launch_cache::binary_format::Closure* closBuf = entry.second;
+ const dyld3::closure::LaunchClosure* closure = entry.second;
closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
- dyld3::launch_cache::Closure closure(closBuf);
- size_t size = closure.size();
+ size_t size = closure->size();
assert((size % 4) == 0);
- memcpy(closuresBase+currentClosureOffset, closBuf, size);
+ memcpy(closuresBase+currentClosureOffset, closure, size);
currentClosureOffset += size;
freeSpace -= size;
- free((void*)closBuf);
+ closure->deallocate();
}
- _buffer->header.progClosuresSize = currentClosureOffset;
- _currentFileSize += currentClosureOffset;
- freeSpace = _allocatedBufferSize - _currentFileSize;
-
+ cache->header.progClosuresSize = currentClosureOffset;
+ _readOnlyRegion.sizeInUse += currentClosureOffset;
+ freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
// build trie of indexes into closures list
DylibIndexTrie closureTrie(closureEntrys);
std::vector<uint8_t> trieBytes;
_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();
+ memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
+ cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ cache->header.progClosuresTrieSize = trieBytes.size();
+ _readOnlyRegion.sizeInUse += trieBytes.size();
+ _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
+}
+
+
+bool CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset))
+{
+ const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset);
+ assert(_readExecuteRegion.sizeInUse == mappings[0].size);
+ assert(_readWriteRegion.sizeInUse == mappings[1].size);
+ assert(_readOnlyRegion.sizeInUse == mappings[2].size);
+ assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
+ assert(_readWriteRegion.cacheFileOffset == mappings[1].fileOffset);
+ assert(_readOnlyRegion.cacheFileOffset == mappings[2].fileOffset);
+ assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize);
+ assert(cacheHeader->codeSignatureOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
+ cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
+ bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
+ fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
+ fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
+ if ( _localSymbolsRegion.sizeInUse != 0 ) {
+ assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
+ fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+ }
+ fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
+ return fullyWritten;
+}
+
+
+void CacheBuilder::writeFile(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 ) {
+ auto cacheSizeCallback = ^(uint64_t size) {
+ ::ftruncate(fd, size);
+ };
+ auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+ uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
+ return writtenSize == size;
+ };
+ bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+ if ( fullyWritten ) {
+ ::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; // success
+ }
+ }
+ else {
+ _diagnostics.error("could not write file %s", pathTemplateSpace);
+ }
+ ::close(fd);
+ ::unlink(pathTemplateSpace);
+ }
+ else {
+ _diagnostics.error("could not open file %s", pathTemplateSpace);
+ }
}
+void CacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
+ auto cacheSizeCallback = ^(uint64_t size) {
+ buffer = (uint8_t*)malloc(size);
+ bufferSize = size;
+ };
+ auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+ memcpy(buffer + dstOffset, src, size);
+ return true;
+ };
+ bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+ assert(fullyWritten);
+}
+
+void CacheBuilder::writeMapFile(const std::string& path)
+{
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ std::string mapContent = cache->mapFile();
+ safeSave(mapContent.c_str(), mapContent.size(), path);
+}
+
+void CacheBuilder::writeMapFileBuffer(uint8_t*& buffer, uint64_t& bufferSize)
+{
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ std::string mapContent = cache->mapFile();
+ buffer = (uint8_t*)malloc(mapContent.size() + 1);
+ bufferSize = mapContent.size() + 1;
+ memcpy(buffer, mapContent.data(), bufferSize);
+}
+
+
+void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
+ for (const DylibInfo& dylibInfo : _sortedDylibs)
+ callback(dylibInfo.runtimePath);
+}
+
+
+CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
+{
+ if ( _bitmap != nullptr )
+ ::free(_bitmap);
+}
+
+void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize)
+{
+ _pageCount = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize;
+ _regionStart = (uint8_t*)rwRegionStart;
+ _endStart = (uint8_t*)rwRegionStart + rwRegionSize;
+ _bitmap = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1);
+}
+
+void CacheBuilder::ASLR_Tracker::add(void* loc)
+{
+ uint8_t* p = (uint8_t*)loc;
+ assert(p >= _regionStart);
+ assert(p < _endStart);
+ _bitmap[(p-_regionStart)/4] = true;
+}
+
+void CacheBuilder::ASLR_Tracker::remove(void* loc)
+{
+ uint8_t* p = (uint8_t*)loc;
+ assert(p >= _regionStart);
+ assert(p < _endStart);
+ _bitmap[(p-_regionStart)/4] = false;
+}
+
+bool CacheBuilder::ASLR_Tracker::has(void* loc)
+{
+ uint8_t* p = (uint8_t*)loc;
+ assert(p >= _regionStart);
+ assert(p < _endStart);
+ return _bitmap[(p-_regionStart)/4];
+}
+
+
+
#include <string>
#include <vector>
+#include <map>
#include <unordered_map>
#include <unordered_set>
+#include "ClosureFileSystem.h"
#include "DyldSharedCache.h"
#include "Diagnostics.h"
-#include "ImageProxy.h"
+#include "MachOAnalyzer.h"
-namespace dyld3 {
- namespace launch_cache {
- namespace binary_format {
- struct ImageGroup;
- struct Closure;
- }
- }
-}
+template <typename P> class LinkeditOptimizer;
+
+
+class CacheBuilder {
+public:
+ CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
+
+ struct InputFile {
+ enum State {
+ Unset,
+ MustBeIncluded,
+ MustBeIncludedForDependent,
+ MustBeExcludedIfUnused
+ };
+ InputFile(const char* path, State state) : path(path), state(state) { }
+ const char* path;
+ State state = Unset;
+ Diagnostics diag;
-struct CacheBuilder {
+ bool mustBeIncluded() const {
+ return (state == MustBeIncluded) || (state == MustBeIncludedForDependent);
+ }
+ };
- CacheBuilder(const DyldSharedCache::CreateOptions& options);
+ // Contains a MachO which has been loaded from the file system and may potentially need to be unloaded later.
+ struct LoadedMachO {
+ DyldSharedCache::MappedMachO mappedFile;
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ InputFile* inputFile;
+ };
- void 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 std::set<const mach_header*> evictions();
- const bool agileSignature();
- const std::string cdHashFirst();
- const std::string cdHashSecond();
+ void build(std::vector<InputFile>& inputFiles,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+ void build(const std::vector<LoadedMachO>& dylibs,
+ const std::vector<LoadedMachO>& otherOsDylibsInput,
+ const std::vector<LoadedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+ void build(const std::vector<DyldSharedCache::MappedMachO>& dylibsToCache,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+ void writeFile(const std::string& path);
+ void writeBuffer(uint8_t*& buffer, uint64_t& size);
+ void writeMapFile(const std::string& path);
+ void writeMapFileBuffer(uint8_t*& buffer, uint64_t& bufferSize);
+ void deleteBuffer();
+ std::string errorMessage();
+ const std::set<std::string> warnings();
+ const std::set<const dyld3::MachOAnalyzer*> evictions();
+ const bool agileSignature();
+ const std::string cdHashFirst();
+ const std::string cdHashSecond();
+
+ void forEachCacheDylib(void (^callback)(const std::string& path));
struct SegmentMappingInfo {
const void* srcSegment;
const char* segName;
- uint64_t dstCacheAddress;
- uint32_t dstCacheOffset;
+ void* dstSegment;
+ uint64_t dstCacheUnslidAddress;
+ uint32_t dstCacheFileOffset;
uint32_t dstCacheSegmentSize;
uint32_t copySegmentSize;
uint32_t srcSegmentIndex;
};
-private:
+ class ASLR_Tracker
+ {
+ public:
+ ~ASLR_Tracker();
+
+ void setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
+ void add(void* p);
+ void remove(void* p);
+ bool has(void* p);
+ const bool* bitmap() { return _bitmap; }
+ unsigned dataPageCount() { return _pageCount; }
+
+ private:
+
+ uint8_t* _regionStart = nullptr;
+ uint8_t* _endStart = nullptr;
+ bool* _bitmap = nullptr;
+ unsigned _pageCount = 0;
+ unsigned _pageSize = 4096;
+ };
+
+ typedef std::map<uint64_t, std::set<void*>> LOH_Tracker;
- typedef std::unordered_map<const mach_header*, std::vector<SegmentMappingInfo>> SegmentMapping;
+ struct Region
+ {
+ uint8_t* buffer = nullptr;
+ uint64_t bufferSize = 0;
+ uint64_t sizeInUse = 0;
+ uint64_t unslidLoadAddress = 0;
+ uint64_t cacheFileOffset = 0;
+ };
+private:
+ template <typename P>
+ friend class LinkeditOptimizer;
+
struct ArchLayout
{
uint64_t sharedMemoryStart;
uint32_t branchPoolLinkEditSize;
uint32_t branchReach;
uint8_t sharedRegionAlignP2;
+ uint8_t slideInfoBytesPerPage;
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);
+ struct UnmappedRegion
+ {
+ uint8_t* buffer = nullptr;
+ uint64_t bufferSize = 0;
+ uint64_t sizeInUse = 0;
+ };
+
+ struct DylibInfo
+ {
+ const LoadedMachO* input;
+ std::string runtimePath;
+ std::vector<SegmentMappingInfo> cacheLocation;
+ };
- SegmentMapping assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, struct dyld_cache_mapping_info regions[3]);
+ void makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
+ void assignSegmentAddresses();
- 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);
+ uint64_t cacheOverflowAmount();
+ size_t evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
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 writeCacheHeader();
+ void copyRawSegments();
+ void adjustAllImagesForNewSegmentLocations();
void writeSlideInfoV1();
- void recomputeCacheUUID(void);
+ void writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
+ uint16_t pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+ void addImageArray();
+ void buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
+ void addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
+ void addClosures(const std::vector<LoadedMachO>&);
+ void markPaddingInaccessible();
+
+ bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
- 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(const bool bitmap[], unsigned dataPageCount);
+ template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+ template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
- 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,
+ template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
+ template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
+ template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+ // implemented in AdjustDylibSegemnts.cpp
+ void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
+
+ // implemented in OptimizerLinkedit.cpp
+ void optimizeLinkedit(const std::vector<uint64_t>& branchPoolOffsets);
+
+ // implemented in OptimizerObjC.cpp
+ void optimizeObjC();
+
+ // implemented in OptimizerBranches.cpp
+ void optimizeAwayStubs(const std::vector<uint64_t>& branchPoolStartAddrs, uint64_t branchPoolsLinkEditStartAddr);
+
+
+ typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
+
const DyldSharedCache::CreateOptions& _options;
- DyldSharedCache* _buffer;
+ const dyld3::closure::FileSystem& _fileSystem;
+ Region _readExecuteRegion;
+ Region _readWriteRegion;
+ Region _readOnlyRegion;
+ UnmappedRegion _localSymbolsRegion;
+ UnmappedRegion _codeSignatureRegion;
+ vm_address_t _fullAllocatedBuffer;
+ uint64_t _nonLinkEditReadOnlySize;
Diagnostics _diagnostics;
- std::set<const mach_header*> _evictions;
+ std::set<const dyld3::MachOAnalyzer*> _evictions;
const ArchLayout* _archLayout;
uint32_t _aliasCount;
uint64_t _slideInfoFileOffset;
uint64_t _slideInfoBufferSizeAllocated;
uint64_t _allocatedBufferSize;
- uint64_t _currentFileSize;
- uint64_t _vmSize;
+ std::vector<DylibInfo> _sortedDylibs;
+ InstallNameToMA _installNameToCacheDylib;
std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
- std::vector<void*> _pointersForASLR;
- dyld3::ImageProxyGroup::PatchTable _patchTable;
+ // Note this is mutable as the only parallel writes to it are done atomically to the bitmap
+ mutable ASLR_Tracker _aslrTracker;
+ std::map<void*, std::string> _missingWeakImports;
+ mutable LOH_Tracker _lohTracker;
+ const dyld3::closure::ImageArray* _imageArray;
+ uint32_t _sharedStringsPoolVmOffset;
std::vector<uint64_t> _branchPoolStarts;
uint64_t _branchPoolsLinkEditStartAddr;
uint8_t _cdHashFirst[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)
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
#include <set>
#include <string>
#include <vector>
#endif
#define NO_ULEB
-#include "MachOParser.h"
+#include "MachOLoaded.h"
+#include "ClosureFileSystemPhysical.h"
#include "CacheBuilder.h"
#include "DyldSharedCache.h"
-#include "LaunchCache.h"
#include "Trie.hpp"
#include "StringUtils.h"
+#include "FileUtils.h"
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
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);
+ const char* prefix = nullptr;
+ if ( (options.pathPrefixes.size() == 1) && !options.pathPrefixes[0].empty() )
+ prefix = options.pathPrefixes[0].c_str();
+ // FIXME: This prefix will be applied to dylib closures and executable closures, even though
+ // the old code didn't have a prefix on cache dylib closures
+ dyld3::closure::FileSystemPhysical fileSystem(prefix);
+ CacheBuilder cache(options, fileSystem);
+ if (!cache.errorMessage().empty()) {
+ results.errorMessage = cache.errorMessage();
+ return results;
+ }
- cache.build(dylibsToCache, otherOsDylibs, osExecutables);
+ std::vector<FileAlias> aliases;
+ switch ( options.platform ) {
+ case dyld3::Platform::iOS:
+ case dyld3::Platform::watchOS:
+ case dyld3::Platform::tvOS:
+ // FIXME: embedded cache builds should be getting aliases from manifest
+ aliases.push_back({"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", "/System/Library/Frameworks/IOKit.framework/IOKit"});
+ aliases.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.dylib"});
+ aliases.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.6.0.9.dylib"});
+ aliases.push_back({"/usr/lib/libz.1.dylib", "/usr/lib/libz.dylib"});
+ aliases.push_back({"/usr/lib/libSystem.B.dylib", "/usr/lib/libSystem.dylib"});
+ break;
+ default:
+ break;
+ }
- results.agileSignature = cache.agileSignature();
- results.cdHashFirst = cache.cdHashFirst();
- results.cdHashSecond = cache.cdHashSecond();
- results.warnings = cache.warnings();
- results.evictions = cache.evictions();
+ cache.build(dylibsToCache, otherOsDylibs, osExecutables, aliases);
+ results.agileSignature = cache.agileSignature();
+ results.cdHashFirst = cache.cdHashFirst();
+ results.cdHashSecond = cache.cdHashSecond();
+ results.warnings = cache.warnings();
+ results.evictions = cache.evictions();
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();
+ if ( !options.outputFilePath.empty() ) {
+ // write cache file, if path non-empty
+ cache.writeFile(options.outputFilePath);
+ }
+ if ( !options.outputMapFilePath.empty() ) {
+ // write map file, if path non-empty
+ cache.writeMapFile(options.outputMapFilePath);
+ }
}
+ results.errorMessage = cache.errorMessage();
+ cache.deleteBuffer();
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)) {
+ if ( dylib.mh->canBePlacedInDyldCache(dylib.runtimePath.c_str(), ^(const char* msg) { badDylibs[dylib.runtimePath].insert(msg);}) ) {
knownDylibs.insert(dylib.runtimePath);
- knownDylibs.insert(parser.installName());
- } else {
- badDylibs[dylib.runtimePath] = reasons;
+ knownDylibs.insert(dylib.mh->installName());
}
}
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) {
+ dylib.mh->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);
- }
+ badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'");
knownDylibs.erase(dylib.runtimePath);
- dyld3::MachOParser parserBad(dylib.mh);
- knownDylibs.erase(parserBad.installName());
+ knownDylibs.erase(dylib.mh->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());
+ if ( foundMapping.mh->canBePlacedInDyldCache(foundMapping.runtimePath.c_str(), ^(const char* msg) { badDylibs[foundMapping.runtimePath].insert(msg);})) {
+ // see if existing mapping was returned
+ bool alreadyInVector = false;
+ for (const MappedMachO& existing : dylibsToCache) {
+ if ( existing.mh == foundMapping.mh ) {
+ alreadyInVector = true;
+ break;
+ }
}
+ if ( !alreadyInVector )
+ foundMappings.push_back(foundMapping);
+ knownDylibs.insert(loadPath);
+ knownDylibs.insert(foundMapping.runtimePath);
+ knownDylibs.insert(foundMapping.mh->installName());
}
}
}
}
}
+bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) const
+{
+ // quick out if before start of cache
+ if ( addr < this )
+ return false;
+
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ uintptr_t unslidStart = (uintptr_t)addr - slide;
+
+ // quick out if after end of cache
+ if ( unslidStart > (mappings[2].address + mappings[2].size) )
+ return false;
+
+ // walk cache regions
+ const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
+ uintptr_t unslidEnd = unslidStart + length;
+ for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
+ if ( (unslidStart >= m->address) && (unslidEnd < (m->address+m->size)) ) {
+ readOnly = ((m->initProt & VM_PROT_WRITE) == 0);
+ return true;
+ }
+ }
+
+ return false;
+}
+
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);
}
}
-void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const
+const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, 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);
+ mTime = dylibs[index].modTime;
+ inode = dylibs[index].inode;
+ return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
+}
+
+void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop)) const
{
// check for old cache without imagesText array
if ( header.mappingOffset < 123 )
// 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);
+ bool stop = false;
+ for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd && !stop; ++p) {
+ handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset, stop);
}
}
+bool DyldSharedCache::addressInText(uint32_t cacheOffset, uint32_t* imageIndex) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( cacheOffset > mappings[0].size )
+ return false;
+ uint64_t targetAddr = mappings[0].address + cacheOffset;
+ // 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) {
+ if ( (p->loadAddress <= targetAddr) && (targetAddr < p->loadAddress+p->textSegmentSize) ) {
+ *imageIndex = (uint32_t)(p-imagesText);
+ return true;
+ }
+ }
+ return false;
+}
-std::string DyldSharedCache::archName() const
+const char* DyldSharedCache::archName() const
{
const char* archSubString = ((char*)this) + 8;
while (*archSubString == ' ')
}
-uint32_t DyldSharedCache::platform() const
+dyld3::Platform DyldSharedCache::platform() const
{
- return header.platform;
+ return (dyld3::Platform)header.platform;
}
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
std::string DyldSharedCache::mapFile() const
{
__block std::string result;
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) {
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
+ mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
char lineBuffer[256];
- sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", segName, vmAddr, vmAddr+vmSize);
+ sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
result += lineBuffer;
});
result += "\n";
return (endAddr - startAddr);
}
+bool DyldSharedCache::findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ uint64_t unslidMh = (uintptr_t)mh - slide;
+ const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+ for (uint32_t i=0; i < header.imagesCount; ++i) {
+ if ( dylibs[i].address == unslidMh ) {
+ imageIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+bool DyldSharedCache::hasImagePath(const char* dylibPath, uint32_t& imageIndex) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( mappings[0].fileOffset != 0 )
+ return false;
+ if ( header.mappingOffset >= 0x118 ) {
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const uint8_t* dylibTrieStart = (uint8_t*)(this->header.dylibsTrieAddr + slide);
+ const uint8_t* dylibTrieEnd = dylibTrieStart + this->header.dylibsTrieSize;
+
+ Diagnostics diag;
+ const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, dylibPath);
+ if ( imageNode != NULL ) {
+ imageIndex = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
+ return true;
+ }
+ }
+ else {
+ const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+ uint64_t firstImageOffset = 0;
+ uint64_t firstRegionAddress = mappings[0].address;
+ for (uint32_t i=0; i < header.imagesCount; ++i) {
+ const char* aPath = (char*)this + dylibs[i].pathFileOffset;
+ if ( strcmp(aPath, dylibPath) == 0 ) {
+ imageIndex = i;
+ return true;
+ }
+ uint64_t offset = dylibs[i].address - firstRegionAddress;
+ if ( firstImageOffset == 0 )
+ firstImageOffset = offset;
+ // skip over aliases
+ if ( dylibs[i].pathFileOffset < firstImageOffset)
+ continue;
+ }
+ }
+ return false;
+}
+
+const dyld3::closure::Image* DyldSharedCache::findDlopenOtherImage(const char* path) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ if ( mappings[0].fileOffset != 0 )
+ return nullptr;
+ if ( header.mappingOffset < sizeof(dyld_cache_header) )
+ return nullptr;
+ if ( header.otherImageArrayAddr == 0 )
+ return nullptr;
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const uint8_t* dylibTrieStart = (uint8_t*)(this->header.otherTrieAddr + slide);
+ const uint8_t* dylibTrieEnd = dylibTrieStart + this->header.otherTrieSize;
+
+ Diagnostics diag;
+ const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, path);
+ if ( imageNode != NULL ) {
+ dyld3::closure::ImageNum imageNum = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
+ uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+ const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+ return otherImageArray->imageForNum(imageNum);
+ }
+
+ return nullptr;
+}
+
+
+
+
+const dyld3::closure::LaunchClosure* DyldSharedCache::findClosure(const char* executablePath) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const uint8_t* executableTrieStart = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
+ const uint8_t* executableTrieEnd = executableTrieStart + this->header.progClosuresTrieSize;
+ const uint8_t* closuresStart = (uint8_t*)(this->header.progClosuresAddr + slide);
+
+ Diagnostics diag;
+ const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, executablePath);
+ if ( (imageNode == NULL) && (strncmp(executablePath, "/System/", 8) == 0) ) {
+ // anything in /System/ should have a closure. Perhaps it was launched via symlink path
+ char realPath[PATH_MAX];
+ if ( realpath(executablePath, realPath) != NULL )
+ imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, realPath);
+ }
+ if ( imageNode != NULL ) {
+ uint32_t closureOffset = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, executableTrieEnd);
+ if ( closureOffset < this->header.progClosuresSize )
+ return (dyld3::closure::LaunchClosure*)((uint8_t*)closuresStart + closureOffset);
+ }
+
+ return nullptr;
+}
+
+#if !BUILDING_LIBDYLD && !BUILDING_DYLD
+void DyldSharedCache::forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const uint8_t* executableTrieStart = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
+ const uint8_t* executableTrieEnd = executableTrieStart + this->header.progClosuresTrieSize;
+ const uint8_t* closuresStart = (uint8_t*)(this->header.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 < this->header.progClosuresSize )
+ handler(entry.name.c_str(), (const dyld3::closure::LaunchClosure*)(closuresStart+offset));
+ }
+ }
+}
+
+void DyldSharedCache::forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const
+{
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+ const uint8_t* otherTrieStart = (uint8_t*)(this->header.otherTrieAddr + slide);
+ const uint8_t* otherTrieEnd = otherTrieStart + this->header.otherTrieSize;
+
+ std::vector<DylibIndexTrie::Entry> otherEntries;
+ if ( Trie<DylibIndex>::parseTrie(otherTrieStart, otherTrieEnd, otherEntries) ) {
+ for (const DylibIndexTrie::Entry& entry : otherEntries ) {
+ dyld3::closure::ImageNum imageNum = entry.info.index;
+ uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+ const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+ handler(entry.name.c_str(), otherImageArray->imageForNum(imageNum));
+ }
+ }
+}
+#endif
+
+const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() const
+{
+ // check for old cache without imagesArray
+ if ( header.mappingOffset < 0x100 )
+ return nullptr;
+
+ if ( header.dylibsImageArrayAddr == 0 )
+ return nullptr;
+
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uint64_t arrayAddrOffset = header.dylibsImageArrayAddr - mappings[0].address;
+ return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+}
+
+const dyld3::closure::ImageArray* DyldSharedCache::otherOSImageArray() const
+{
+ // check for old cache without imagesArray
+ if ( header.mappingOffset < sizeof(dyld_cache_header) )
+ return nullptr;
+
+ if ( header.otherImageArrayAddr == 0 )
+ return nullptr;
+
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+ uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+ return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+}
#include <string>
#include <vector>
#include <unordered_map>
+#include <uuid/uuid.h>
#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;
- }
- }
-}
+#include "MachOAnalyzer.h"
+#include "Closure.h"
class VIS_HIDDEN DyldSharedCache
struct CreateOptions
{
+ std::string outputFilePath;
+ std::string outputMapFilePath;
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 isLocallyBuiltCache;
bool verbose;
bool evictLeafDylibsOnOverflow;
std::unordered_map<std::string, unsigned> dylibOrdering;
{
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)
+ MappedMachO(const std::string& path, const dyld3::MachOAnalyzer* 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;
+ const dyld3::MachOAnalyzer* mh;
size_t length;
uint64_t isSetUID : 1,
protectedBySIP : 1,
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;
- std::set<const mach_header*> evictions;
- bool agileSignature = false;
- std::string cdHashFirst;
- std::string cdHashSecond;
+ std::string errorMessage;
+ std::set<std::string> warnings;
+ std::set<const dyld3::MachOAnalyzer*> evictions;
+ bool agileSignature = false;
+ std::string cdHashFirst;
+ std::string cdHashSecond;
+ };
+
+
+ struct FileAlias
+ {
+ std::string realPath;
+ std::string aliasPath;
};
//
// Returns the architecture name of the shared cache, e.g. "arm64"
//
- std::string archName() const;
+ const char* archName() const;
//
// Returns the platform the cache is for
//
- uint32_t platform() const;
+ dyld3::Platform platform() const;
//
//
+ // Searches cache for dylib with specified path
+ //
+ bool hasImagePath(const char* dylibPath, uint32_t& imageIndex) const;
+
+
+ //
+ // Searches cache for dylib with specified mach_header
+ //
+ bool findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) 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;
+ const mach_header* getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& node) 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, bool& stop)) const;
//
void forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const;
+ //
+ // Returns if an address range is in this cache, and if so if in a read-only area
+ //
+ bool inCache(const void* addr, size_t length, bool& readOnly) const;
+
+
//
// returns address the cache would load at if unslid
//
uint64_t mappedSize() const;
+ //
+ // searches cache for pre-built closure for program
+ //
+ const dyld3::closure::LaunchClosure* findClosure(const char* executablePath) const;
+
+
+ //
+ // iterates all pre-built closures for program
+ //
+ void forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const;
+
+
+ //
+ // iterates all pre-built Image* for OS dylibs/bundles not in dyld cache
+ //
+ void forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const;
+
+
+ //
+ // returns the ImageArray pointer to Images in dyld shared cache
+ //
+ const dyld3::closure::ImageArray* cachedDylibsImageArray() const;
+
+
+ //
+ // returns the ImageArray pointer to Images in OS with pre-build dlopen closure
+ //
+ const dyld3::closure::ImageArray* otherOSImageArray() const;
+
+
+ //
+ // searches cache for pre-built dlopen closure for OS dylib/bundle
+ //
+ const dyld3::closure::Image* findDlopenOtherImage(const char* path) const;
+
+
+ //
+ // returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index
+ //
+ bool addressInText(uint32_t cacheOffset, uint32_t* index) const;
+
+
+
dyld_cache_header header;
};
#include <sstream>
#include "FileUtils.h"
+#include "StringUtils.h"
#include "Diagnostics.h"
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
#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)
+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, bool recurse)
{
std::string fullDirPath = pathPrefix + path;
DIR* dir = ::opendir(fullDirPath.c_str());
break;
if ( dirFilter(dirAndFile) )
break;
- iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
+ if (recurse)
+ iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback, processFiles, true);
break;
case DT_LNK:
// don't follow symlinks, dylib will be found through absolute path
int fd = mkstemp(pathTemplateSpace);
if ( fd != -1 ) {
ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
- if ( writtenSize == bufferLen ) {
+ if ( (size_t)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 false; // failure
}
-const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
+const void* mapFileReadOnly(const char* path, size_t& mappedSize)
{
struct stat statBuf;
- if ( ::stat(path.c_str(), &statBuf) != 0 )
+ if ( ::stat(path, &statBuf) != 0 )
return nullptr;
- int fd = ::open(path.c_str(), O_RDONLY);
+ int fd = ::open(path, O_RDONLY);
if ( fd < 0 )
return nullptr;
//
// 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> parseOrderFile(const std::string& orderFileData) {
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++;
+ if (orderFileData.empty())
+ return order;
+
+ std::stringstream myData(orderFileData);
+
+ uint32_t count = 0;
+ std::string line;
+ while ( std::getline(myData, line) ) {
+ size_t pos = line.find('#');
+ if ( pos != std::string::npos )
+ line.resize(pos);
+ while ( !line.empty() && isspace(line.back()) ) {
+ line.pop_back();
}
- myfile.close();
+ if ( !line.empty() )
+ order[line] = count++;
+ }
+ return order;
+}
+
+std::string loadOrderFile(const std::string& orderFilePath) {
+ std::string order;
+
+ size_t size = 0;
+ char* data = (char*)mapFileReadOnly(orderFilePath.c_str(), size);
+ if (data) {
+ order = std::string(data, size);
+ ::munmap((void*)data, size);
}
return order;
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;
return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
}
+static void normalizePath(std::string& path) {
+ // Remove a bunch of stuff we don't need, like trailing slashes.
+ while ( !path.empty() && (path.back() == '/'))
+ path.pop_back();
+}
+
+void SymlinkResolver::addFile(Diagnostics& diags, std::string path) {
+ if (path.front() != '/') {
+ diags.error("Path must start with '/'");
+ return;
+ }
+ if (symlinks.find(path) != symlinks.end()) {
+ diags.error("Cannot add regular file as it is already a symlink");
+ return;
+ }
+ filePaths.insert(path);
+}
+
+void SymlinkResolver::addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath) {
+ normalizePath(fromPath);
+ normalizePath(toPath);
+ if (fromPath.front() != '/') {
+ diags.error("Path must start with '/'");
+ return;
+ }
+ if (filePaths.find(fromPath) != filePaths.end()) {
+ diags.error("Cannot add symlink from '%s' as it is already a regular path", fromPath.c_str());
+ return;
+ }
+ auto itAndInserted = symlinks.insert({ fromPath, toPath });
+ if (!itAndInserted.second) {
+ // The path is already a symlink. Make sure its a dupe.
+ if (toPath != itAndInserted.first->second) {
+ diags.error("Duplicate symlink for path '%s'", fromPath.c_str());
+ return;
+ }
+ }
+}
+
+std::string SymlinkResolver::realPath(Diagnostics& diags, const std::string& originalPath) const {
+ // First make sure the path doesn't have any magic in it.
+ std::string path = originalPath;
+ normalizePath(path);
+
+ std::set<std::string> seenSymlinks;
+
+ // Now see if any prefix is a symlink
+ if (path.front() != '/')
+ return path;
+
+ std::string::size_type prev_pos = 0;
+ while (prev_pos != std::string::npos) {
+ std::string::size_type pos = path.find("/", prev_pos + 1);
+
+ // First look to see if this path component is special, eg, ., .., etc.
+ std::string component = path.substr(prev_pos, pos - prev_pos);
+ if (component == "/..") {
+ // Fold with the previous path component.
+ if (prev_pos == 0) {
+ // This is the root path, and .. applied to / is just /
+ path = path.substr(3);
+ prev_pos = 0;
+ } else {
+ std::string::size_type lastSlashPos = path.rfind("/", prev_pos - 1);
+ path = path.substr(0, lastSlashPos) + path.substr(pos);
+ prev_pos = lastSlashPos;
+ }
+ continue;
+ } else if (component == "/.") {
+ if (prev_pos == 0) {
+ // Path starts with /./ so just remove the first one.
+ path = path.substr(2);
+ } else {
+ if (pos == std::string::npos) {
+ // Trailing . on the path
+ path = path.substr(0, prev_pos );
+ } else {
+ path = path.substr(0, prev_pos) + path.substr(pos);
+ }
+ }
+ continue;
+ } else if (component == "/") {
+ // Path must contain // somewhere so strip out the duplicates.
+ if (prev_pos == 0) {
+ // Path starts with // so just remove the first one.
+ path = path.substr(1);
+ } else {
+ if (pos == std::string::npos) {
+ // Trailing / on the path
+ path = path.substr(0, prev_pos);
+ prev_pos = pos;
+ } else {
+ path = path.substr(0, pos) + path.substr(pos + 1);
+ }
+ }
+ continue;
+ }
+
+ // Path is not special, so see if it is a symlink to something.
+ std::string prefix = path.substr(0, pos);
+ //printf("%s\n", prefix.c_str());
+ auto it = symlinks.find(prefix);
+ if (it == symlinks.end()) {
+ // This is not a symlink so move to the next prefix.
+ prev_pos = pos;
+ continue;
+ }
+
+ // If we've already done this prefix then error out.
+ if (seenSymlinks.count(prefix)) {
+ diags.error("Loop in symlink processing for '%s'", originalPath.c_str());
+ return std::string();
+ }
+
+ seenSymlinks.insert(prefix);
+
+ // This is a symlink, so resolve the new path.
+ std::string toPath = it->second;
+ if (toPath.front() == '/') {
+ // Symlink points to an absolute address so substitute the whole prefix for the new path
+ // If we didn't substitute the last component of the path then there is also a path suffix.
+ std::string pathSuffix = "";
+ if (pos != std::string::npos) {
+ std::string::size_type nextSlashPos = path.find("/", pos + 1);
+ if (nextSlashPos != std::string::npos)
+ pathSuffix = path.substr(nextSlashPos);
+ }
+ path = toPath + pathSuffix;
+ prev_pos = 0;
+ continue;
+ }
+
+ // Symlink points to a relative path so we need to do more processing to get the real path.
+
+ // First calculate which part of the previous prefix we'll keep. Eg, in /a/b/c where "b -> blah", we want to keep /a here.
+ std::string prevPrefix = path.substr(0, prev_pos);
+ //printf("prevPrefix %s\n", prevPrefix.c_str());
+
+ // If we didn't substitute the last component of the path then there is also a path suffix.
+ std::string pathSuffix = "";
+ if (prefix.size() != path.size())
+ pathSuffix = path.substr(pos);
+
+ // The new path is the remaining prefix, plus the symlink target, plus any remaining suffix from the original path.
+ path = prevPrefix + "/" + toPath + pathSuffix;
+ prev_pos = 0;
+ }
+ return path;
+}
+
+std::vector<DyldSharedCache::FileAlias> SymlinkResolver::getResolvedSymlinks(Diagnostics& diags) {
+ diags.assertNoError();
+ std::vector<DyldSharedCache::FileAlias> aliases;
+ for (auto& fromPathAndToPath : symlinks) {
+ std::string newPath = realPath(diags, fromPathAndToPath.first);
+ if (diags.hasError()) {
+ aliases.clear();
+ return aliases;
+ }
+
+ if (filePaths.count(newPath)) {
+ aliases.push_back({ newPath, fromPathAndToPath.first });
+ // printf("symlink ('%s' -> '%s') resolved to '%s'\n", fromPathAndToPath.first.c_str(), fromPathAndToPath.second.c_str(), newPath.c_str());
+ }
+ }
+ return aliases;
+}
+
#endif // BUILDING_CACHE_BUILDER
#include <stdint.h>
+#include <map>
+#include <set>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <dispatch/dispatch.h>
+#include "DyldSharedCache.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);
// 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);
+ void (^callback)(const std::string& path, const struct stat& statBuf), bool processFiles=true, bool recurse=true);
//
bool safeSave(const void* buffer, size_t bufferLen, const std::string& path);
-const void* mapFileReadOnly(const std::string& path, size_t& mappedSize);
+const void* mapFileReadOnly(const char* 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::unordered_map<std::string, uint32_t> parseOrderFile(const std::string& orderFileData);
+std::string loadOrderFile(const std::string& orderFilePath);
std::string normalize_absolute_file_path(std::string path);
std::string basePath(const std::string& path);
std::string toolDir();
+class SymlinkResolver {
+public:
+ SymlinkResolver() { }
+
+ void addFile(Diagnostics& diags, std::string path);
+
+ void addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath);
+ std::string realPath(Diagnostics& diags, const std::string& path) const;
+ std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diags);
+
+private:
+ std::set<std::string> filePaths;
+ std::map<std::string, std::string> symlinks;
+};
#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++, true);
- 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, true);
- 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));
- if ( replacementOffsetInImage >= 0xFFFFFFFFULL ) {
- diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
- return;
- }
- 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));
- if ( foundInfo.value >= (1ULL << 32) ) {
- diag.warning("bad weak symbol address in %s", proxy->runtimePath().c_str());
- return;
- }
- 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
#ifndef CPU_TYPE_ARM64
#define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64))
#endif
+#ifndef CPU_TYPE_ARM64_32
+ #ifndef CPU_ARCH_ABI64_32
+ #define CPU_ARCH_ABI64_32 0x02000000
+ #endif
+ #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+ #define CPU_SUBTYPE_ARM64_32_V8 1
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
#define ARM64_RELOC_UNSIGNED 0 // for pointers
#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A
#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B
#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C
+#define DYLD_CACHE_ADJ_V2_THREADED_POINTER_64 0x0D
#define MH_HAS_OBJC 0x40000000
} while (byte & 0x80);
// sign extend negative numbers
if ( (byte & 0x40) != 0 )
- result |= (-1LL) << bit;
+ result |= (~0ULL) << bit;
return result;
}
#import <Foundation/Foundation.h>
-#include "MachOParser.h"
#include "DyldSharedCache.h"
#include "Diagnostics.h"
+#include "MachOAnalyzer.h"
extern std::string toolDir();
namespace dyld3 {
+struct VIS_HIDDEN UUID {
+ UUID() {}
+ UUID(const UUID& other) { uuid_copy(_bytes, other._bytes); }
+ UUID(const uuid_t other) { uuid_copy(&_bytes[0], other); }
+ UUID(const dyld3::MachOAnalyzer* ml) { ml->getUuid(_bytes); }
+ bool operator<(const UUID& other) const { return uuid_compare(_bytes, other._bytes) < 0; }
+ bool operator==(const UUID& other) const { return uuid_compare(_bytes, other._bytes) == 0; }
+ bool operator!=(const UUID& other) const { return !(*this == other); }
+
+ size_t hash() const
+ {
+ size_t retval = 0;
+ for (size_t i = 0; i < (16 / sizeof(size_t)); ++i) {
+ retval ^= ((size_t*)(&_bytes[0]))[i];
+ }
+ return retval;
+ }
+ const unsigned char* get() const { return _bytes; };
+
+private:
+ uuid_t _bytes;
+};
+
struct BuildQueueEntry {
DyldSharedCache::CreateOptions options;
std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
struct Manifest {
struct UUIDInfo {
- const mach_header* mh;
+ const MachOAnalyzer* mh;
uint64_t sliceFileOffset;
std::size_t size;
std::string runtimePath;
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)
+ UUIDInfo(const MachOAnalyzer* 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(), "", "", "", "") {}
};
std::vector<std::string> sources;
};
- struct File {
- MachOParser* parser;
- File(MachOParser* P)
- : parser(P)
- {
- }
- };
struct SegmentInfo {
std::string name;
CacheInfo developmentCache;
CacheInfo productionCache;
CacheImageInfo& dylibForInstallname(const std::string& installname);
- void exclude(MachOParser* parser, const std::string& reason);
+ void exclude(const dyld3::MachOAnalyzer* ml, const std::string& reason);
void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
};
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);
+ Manifest(Diagnostics& D, const std::string& path, bool onlyParseManifest = false);
+ Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays, bool onlyParseManifest = false);
- BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose);
+ BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix,
+ bool isLocallyBuiltCache, bool skipWrites, 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 MachOAnalyzer* machOForUUID(const UUID& uuid) const;
const std::string buildPathForUUID(const UUID& uuid);
const std::string runtimePathForUUID(const UUID& uuid);
+ const std::string& installNameForUUID(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);
+ std::set<std::string> resultsForConfiguration(const std::string& configName);
+
+ // These are used by MRM to support having the Manifest give us a list of files/symlinks from the BOM but we use MRM for the actual cache generation
+ void forEachMachO(std::string configuration, std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda);
+
+ void forEachSymlink(std::string configuration, std::function<void(const std::string &fromPath, const std::string &toPath)> lambda);
private:
NSDictionary* _manifestDict;
Diagnostics& _diags;
std::map<UUID, UUIDInfo> _uuidMap;
std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
+ std::vector<std::pair<std::string, std::string>> _symlinks;
static dispatch_queue_t _identifierQueue;
uint32_t _manifestVersion;
std::string _build;
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>> _metabomSymlinkTagMap;
std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
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 loadParser(const void* p, size_t sliceLength, 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);
};
}
+namespace std {
+template <>
+struct hash<dyld3::UUID> {
+ size_t operator()(const dyld3::UUID& x) const
+ {
+ return x.hash();
+ }
+};
+}
+
#endif /* Manifest_h */
#include "Trie.hpp"
#include "FileUtils.h"
#include "StringUtils.h"
+#include "MachOFile.h"
+#include "MachOAnalyzer.h"
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <vector>
#include "Manifest.h"
+#include "ClosureFileSystemPhysical.h"
namespace {
//FIXME this should be in a class
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)
+void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason)
{
- auto dylibUUID = parser->uuid();
- dylibs[dylibUUID].uuid = dylibUUID;
- dylibs[dylibUUID].installname = parser->installName();
- dylibs[dylibUUID].included = false;
+ UUID dylibUUID(mh);
+ dylibs[dylibUUID].uuid = dylibUUID;
+ dylibs[dylibUUID].installname = mh->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;
+ const MachOAnalyzer* mh = manifest.machOForUUID(uuid);
+ dylibs[uuid].uuid = uuid;
+ dylibs[uuid].installname = mh->installName();
+ dylibs[uuid].included = false;
dylibs[uuid].exclusionInfo = reason;
}
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)
+BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs,
+ const std::string& prefix, bool isLocallyBuiltCache, bool skipWrites, bool verbose)
{
dyld3::BuildQueueEntry retval;
DyldSharedCache::CreateOptions options;
+ options.outputFilePath = skipWrites ? "" : outputPath;
+ options.outputMapFilePath = skipWrites ? "" : outputPath + ".map";
options.archName = arch;
options.platform = platform();
options.excludeLocalSymbols = true;
options.inodesAreSameAsRuntime = false;
options.cacheSupportsASLR = true;
options.forSimulator = false;
+ options.isLocallyBuiltCache = isLocallyBuiltCache;
options.verbose = verbose;
options.evictLeafDylibsOnOverflow = true;
options.loggingPrefix = prefix;
- options.pathPrefixes = { "" };
- options.dylibOrdering = loadOrderFile(_dylibOrderFile);
- options.dirtyDataSegmentOrdering = loadOrderFile(_dirtyDataOrderFile);
+ options.pathPrefixes = { "./Root/" };
+ options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
+ options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
dyld3::BuildQueueEntry queueEntry;
retval.configNames = configs;
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)
+bool Manifest::loadParser(const void* p, size_t sliceLength, 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)) {
+ assert(!_diags.hasError());
+
+ const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
+ const std::string archName = mf->archName();
+ if ( archName == "unknown" ) {
+ // Clear the error and punt
+ _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
return false;
}
+ if ( architectures.count(archName) == 0 )
+ return false;
- auto parser = MachOParser(mh);
- if (_diags.hasError()) {
+ const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
+ if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), archName.c_str(), _platform) ) {
// Clear the error and punt
- _diags.verbose("MachoParser error: %s\n", _diags.errorMessage().c_str());
+ _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+ _diags.clearError();
+ return false;
+ }
+
+ // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
+ // remapIfZeroFill() will remap the file
+ closure::FileSystemPhysical fileSystem;
+ closure::LoadedFileInfo info;
+ info.fileContent = p;
+ info.fileContentLen = sliceLength;
+ info.sliceOffset = 0;
+ info.sliceLen = sliceLength;
+ info.inode = 0;
+ info.mtime = 0;
+ info.unload = nullptr;
+ ma = ma->remapIfZeroFill(_diags, fileSystem, info);
+
+ if (ma == nullptr) {
+ _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
_diags.clearError();
return false;
}
- auto uuid = parser.uuid();
- auto archName = parser.archName();
+ uuid_t uuid;
+ ma->getUuid(uuid);
- if (parser.fileType() == MH_DYLIB && architectures.count(parser.archName()) != 0) {
- std::string installName = parser.installName();
- auto index = std::make_pair(installName, parser.archName());
+ if ( ma->isDylib() ) {
+ std::string installName = ma->installName();
+ auto index = std::make_pair(installName, 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)));
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, 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)));
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
if (installName[0] != '@' && installName != runtimePath) {
_diags.warning("Dylib located at '%s' has installname '%s'", runtimePath.c_str(), installName.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)));
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
}
}
} else {
- _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, "")));
+ _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
}
return true;
}
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 ( const FatFile* fh = FatFile::isFatFile(p) ) {
+ fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
retval = true;
});
return retval;
}
+
const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
auto i = _uuidMap.find(uuid);
assert(i != _uuidMap.end());
return i->second;
}
-MachOParser Manifest::parserForUUID(const UUID& uuid) const {
- return MachOParser(infoForUUID(uuid).mh);
+const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
+ return infoForUUID(uuid).mh;
}
const std::string Manifest::buildPathForUUID(const UUID& uuid) {
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>())
+
+const std::string& Manifest::installNameForUUID(const UUID& uuid) {
+ return infoForUUID(uuid).installName;
+}
+
+
+Manifest::Manifest(Diagnostics& D, const std::string& path, bool onlyParseManifest) : Manifest(D, path, std::set<std::string>(), onlyParseManifest)
{
}
-Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays) :
+Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays, bool onlyParseManifest) :
_diags(D)
{
- NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
- NSString* platStr = manifestDict[@"platform"];
+ _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
+ if (!_manifestDict)
+ return;
+ NSString* platStr = _manifestDict[@"platform"];
std::set<std::string> architectures;
if (platStr == nullptr)
platStr = @"ios";
std::string platformString = [platStr UTF8String];
- setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
+ setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
if (platformString == "ios") {
setPlatform(dyld3::Platform::iOS);
setPlatform(dyld3::Platform::iOS);
}
- for (NSString* project in manifestDict[@"projects"]) {
- for (NSString* source in manifestDict[@"projects"][project]) {
+ for (NSString* project in _manifestDict[@"projects"]) {
+ for (NSString* source in _manifestDict[@"projects"][project]) {
addProjectSource([project UTF8String], [source UTF8String]);
}
}
- for (NSString* configuration in manifestDict[@"configurations"]) {
+ for (NSString* configuration in _manifestDict[@"configurations"]) {
std::string configStr = [configuration UTF8String];
- std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
+ std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
- if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
- for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
+ 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"]) {
+ 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];
+ [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
if (endsWith(configStr, "InternalOS")) {
_configurations[configStr].disposition = "internal";
_configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
}
- for (NSString* architecutre in manifestDict[@"configurations"][configuration][@"architectures"]) {
+ 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();
}
}
- setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
- setBuild([manifestDict[@"build"] UTF8String]);
- if (manifestDict[@"dylibOrderFile"]) {
- setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
+ setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
+ setBuild([_manifestDict[@"build"] UTF8String]);
+ if (_manifestDict[@"dylibOrderFile"]) {
+ setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
}
- if (manifestDict[@"dirtyDataOrderFile"]) {
- setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
+ if (_manifestDict[@"dirtyDataOrderFile"]) {
+ setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
}
+ if (onlyParseManifest)
+ return;
+
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);
MBTag tag;
auto tagCount = MBEntryGetNumberOfProjectTags(entry);
- if (entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
+ bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
+ bool isSymlinkFile = entryType == BOMSymlinkType;
+ if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
if (tagCount == 1) {
MBEntryGetProjectTags(entry, &tag);
} else {
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 (isObjectFile) {
+ _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
+
+ bool foundParser = false;
+ for (const auto& overlay : overlays) {
+ if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
+ foundParser = true;
+ _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
+ break;
+ }
+ if (_diags.hasError()) {
+ _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+ _diags.clearError();
+ }
}
- }
- if (!foundParser) {
- (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+ if (!foundParser) {
+ (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+ if (_diags.hasError()) {
+ _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+ _diags.clearError();
+ } else {
+ foundParser = true;
+ }
+ }
+ } else if (isSymlinkFile) {
+ const char* target = BOMFSObjectSymlinkTarget(fsObject);
+ _symlinks.push_back({ entryPath, target });
+ _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
}
}
}
const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
for (const auto& dylib : dylibs) {
if (!dylib.second.included) {
- insert(retval, dylib.second);
+ const UUIDInfo& info = infoForUUID(dylib.second.uuid);
+ if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
+ insert(retval, dylib.second);
}
}
const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
for (const auto& bundle : bundles) {
- insert(retval, bundle.second);
+ const UUIDInfo& info = infoForUUID(bundle.second.uuid);
+ if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
+ insert(retval, bundle.second);
}
return retval;
return retval;
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wrange-loop-analysis"
bool Manifest::filterForConfig(const std::string& configName)
{
for (const auto configuration : _configurations) {
}
return false;
}
+#pragma clang diagnostic pop
+
+std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
+ std::set<std::string> results;
+ NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
+ for (NSString* arch in configurationResults) {
+ NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
+ for (NSString* dylib in dylibs) {
+ NSDictionary* dylibDict = dylibs[dylib];
+ if ([dylibDict[@"included"] boolValue])
+ results.insert([dylib UTF8String]);
+ }
+ }
+ return results;
+}
void Manifest::dedupeDispositions(void) {
// Since this is all hacky and inference based for now only do it for iOS until XBS
_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)
{
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);
-
}
}
}
processedUuids.insert(uuid);
- auto parser = parserForUUID(uuid);
+ const MachOAnalyzer* mh = machOForUUID(uuid);
auto runtimePath = runtimePathForUUID(uuid);
- assert(parser.header() != 0);
+ assert(mh != nullptr);
- parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ mh->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) {
+ if (mh->isDylib()) {
// 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();
+ archManifest.results.dylibs[uuid].installname = mh->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");
+ if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
+ || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
+ archManifest.results.exclude(mh, "Device specific dylib");
continue;
}
- std::set<std::string> reasons;
- if (parser.canBePlacedInDyldCache(runtimePath, reasons)) {
+ __block std::set<std::string> reasons;
+ if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
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");
+ archManifest.results.exclude(mh, "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());
+ cachedUUIDs.insert(uuid);
} else {
// It can't be placed in the cache, print out the reasons why
std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
}
}
reasonString += "\")";
- archManifest.results.exclude(&parser, reasonString);
+ archManifest.results.exclude(mh, reasonString);
}
- } else if (parser.fileType() == MH_BUNDLE) {
+ } else if (mh->isBundle()) {
if (archManifest.results.bundles.count(uuid) == 0) {
archManifest.results.bundles[uuid].uuid = uuid;
}
- } else if (parser.fileType() == MH_EXECUTE) {
+ } else if (mh->isMainExecutable()) {
//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"
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) {
+ const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
+ mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
if (isWeak)
return;
}
if (badDependencies.size()) {
- std::string reasonString = "Rejected from cached dylibs: " + std::string(parser.installName()) + " " + architecture + " (\"";
+ std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
reasonString += *i;
if (i != --badDependencies.end()) {
}
}
reasonString += "\")";
- archManifest.results.exclude(&parser, reasonString);
+ archManifest.results.exclude(mh, reasonString);
}
});
}
__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) {
+ const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
+ mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
linkedDylibs.insert(loadPath);
});
}
cacheDict[@"platform"] = @"macos";
break;
case Platform::unknown:
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ case Platform::watchOS_simulator:
cacheDict[@"platform"] = @"unknown";
break;
}
error:&error];
(void)[outData writeToFile:cppToObjStr(path) atomically:YES];
}
+
+
+void Manifest::forEachMachO(std::string configuration,
+ std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
+ for (auto& uuidInfo : _uuidMap) {
+ auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
+ assert(i != _metabomTagMap.end());
+ auto restrictions = _metabomRestrictedTagMap.find(configuration);
+ if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+ continue;
+ }
+ auto& configManifest = _configurations[configuration];
+ auto exclusions = _metabomExcludeTagMap.find(configuration);
+ bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
+ bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
+ bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
+ lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
+ }
+}
+
+
+void Manifest::forEachSymlink(std::string configuration,
+ std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
+ for (const auto& symlink : _symlinks) {
+ auto i = _metabomSymlinkTagMap.find(symlink.first);
+ assert(i != _metabomSymlinkTagMap.end());
+ auto restrictions = _metabomRestrictedTagMap.find(configuration);
+ if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+ continue;
+ }
+ lambda(symlink.first, symlink.second);
+ }
}
+
+} //namespace dyld3
}
}
- static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
+ static void addPointers(uint8_t* methodList, CacheBuilder::ASLR_Tracker& aslrTracker) {
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));
+ aslrTracker.add(&(entry.name));
+ aslrTracker.add(&(entry.types));
+ aslrTracker.add(&(entry.imp));
}
}
}
}
- static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
+ static void addPointers(uint8_t* propertyList, CacheBuilder::ASLR_Tracker& aslrTracker) {
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));
+ aslrTracker.add(&(entry.name));
+ aslrTracker.add(&(entry.attributes));
}
}
P::setP(demangledName, cache->vmAddrForContent((void*)newName));
}
- void addPointers(std::vector<void*>& pointersToAdd)
+ void addPointers(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker)
{
- 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);
+ aslrTracker.add(&isa);
+ aslrTracker.add(&name);
+ if (protocols) aslrTracker.add(&protocols);
+ if (instanceMethods) aslrTracker.add(&instanceMethods);
+ if (classMethods) aslrTracker.add(&classMethods);
+ if (optionalInstanceMethods) aslrTracker.add(&optionalInstanceMethods);
+ if (optionalClassMethods) aslrTracker.add(&optionalClassMethods);
+ if (instanceProperties) aslrTracker.add(&instanceProperties);
+ if (extendedMethodTypes) aslrTracker.add(&extendedMethodTypes);
+ if (demangledName) aslrTracker.add(&demangledName);
}
};
}
}
- static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
+ static void addPointers(uint8_t* protocolList, CacheBuilder::ASLR_Tracker& aslrTracker) {
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]);
+ aslrTracker.add(&plist->list[i]);
}
}
P::setP(baseProperties, cache->vmAddrForContent(proplist));
}
- void addMethodListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseMethods);
+ void addMethodListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+ aslrTracker.add(&this->baseMethods);
}
- void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseProperties);
+ void addPropertyListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+ aslrTracker.add(&this->baseProperties);
}
- void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
- pointersToAdd.push_back(&this->baseProtocols);
+ void addProtocolListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+ aslrTracker.add(&this->baseProtocols);
}
};
objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
+ const pint_t* getSuperClassAddress() const { return &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)); }
getData(cache)->setPropertyList(cache, proplist);
}
- void addMethodListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addMethodListPointer(pointersToAdd);
+ void addMethodListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+ getData(cache)->addMethodListPointer(aslrTracker);
}
- void addPropertyListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addPropertyListPointer(pointersToAdd);
+ void addPropertyListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+ getData(cache)->addPropertyListPointer(aslrTracker);
}
- void addProtocolListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
- getData(cache)->addProtocolListPointer(pointersToAdd);
+ void addProtocolListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+ getData(cache)->addProtocolListPointer(aslrTracker);
}
};
V& mVisitor;
+ std::set<pint_t> selectorRefVMAddrs;
+
friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
void visitMethodList(objc_method_list_t<P> *mlist)
{
pint_t oldValue = selrefs.getVMAddress(i);
pint_t newValue = mVisitor.visit(oldValue);
selrefs.setVMAddress(i, newValue);
+ selectorRefVMAddrs.insert(selrefs.getSectionVMAddress() + (i * sizeof(pint_t)));
}
// message references
msg.setName(newValue);
}
}
+
+ bool isSelectorRefAddress(pint_t vmAddr) const {
+ return selectorRefVMAddrs.count(vmAddr);
+ }
};
// Detect classes that have missing weak-import superclasses.
template <typename P>
class WeakClassDetector {
- bool noMissing;
+ bool noMissing;
+ const std::map<void*, std::string>* missingWeakImports = nullptr;
friend class ClassWalker<P, WeakClassDetector<P>>;
void visitClass(ContentAccessor* cache, const macho_header<P>*,
} 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));
+ // bad: cls's superclass is missing.
+ // See if we can find the name from the missing weak import map
+ auto it = missingWeakImports->find((void*)cls->getSuperClassAddress());
+ const char* dylibName = "unknown dylib";
+ if (it != missingWeakImports->end()) {
+ dylibName = it->second.c_str();
+ }
+ cache->diagnostics().warning("Superclass of class '%s' is weak-import and missing. Expected in %s",
+ cls->getName(cache), dylibName);
noMissing = false;
}
}
public:
- bool noMissingWeakSuperclasses(ContentAccessor* cache,
+ bool noMissingWeakSuperclasses(ContentAccessor* cache,
+ const std::map<void*, std::string>& missingWeakImportsMap,
std::vector<const macho_header<P>*> dylibs)
{
- noMissing = true;
+ noMissing = true;
+ missingWeakImports = &missingWeakImportsMap;
ClassWalker<P, WeakClassDetector<P>> classes(*this);
for (auto mh : dylibs) {
classes.walk(cache, mh);
return nullptr;
}
- void update(ContentAccessor* cache, const macho_header<P>* mh, std::vector<void*>& pointersInData) {
+ void update(ContentAccessor* cache, const macho_header<P>* mh, CacheBuilder::ASLR_Tracker& aslrTracker) {
InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh);
(void)hi;
}
#include "StringUtils.h"
#include "Trie.hpp"
#include "MachOFileAbstraction.hpp"
-#include "MachOParser.h"
+#include "MachOAnalyzer.h"
#include "Diagnostics.h"
#include "DyldSharedCache.h"
#include "CacheBuilder.h"
class BranchPoolDylib {
public:
BranchPoolDylib(DyldSharedCache* cache, uint64_t startAddr,
- uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags);
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditFileOffset, Diagnostics& diags);
uint64_t addr() { return _startAddr; }
uint64_t getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
template <typename P>
BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAddr,
- uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags)
+ uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditFileOffset, 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 int64_t cacheSlide = (long)cache - cache->unslidLoadAddress();
const uint64_t textSegSize = branchPoolTextSize(archName);
const uint64_t linkEditSegSize = branchPoolLinkEditSize(archName);
- const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4);
+ const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/sizeof(uint32_t));
const uint32_t linkeditOffsetSymbolTable = 0;
const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist<P>);
const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t);
// 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_cputype(dyld3::MachOFile::cpuTypeFromArchName(archName.c_str()));
+ mh->set_cpusubtype(dyld3::MachOFile::cpuSubtypeFromArchName(archName.c_str()));
mh->set_filetype(MH_DYLIB);
mh->set_ncmds(6);
mh->set_sizeofcmds(is64 ? 0x210 : 100); // FIXME: 32-bit size
linkEditSegCmd->set_segname("__LINKEDIT");
linkEditSegCmd->set_vmaddr(poolLinkEditStartAddr);
linkEditSegCmd->set_vmsize(linkEditSegSize);
- linkEditSegCmd->set_fileoff(poolLinkEditStartOffset);
+ linkEditSegCmd->set_fileoff(poolLinkEditFileOffset);
linkEditSegCmd->set_filesize(linkEditSegSize);
linkEditSegCmd->set_maxprot(PROT_READ);
linkEditSegCmd->set_initprot(PROT_READ);
_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_symoff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetSymbolTable));
+ _symbolTableCmd->set_stroff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetSymbolPoolOffset));
_symbolTableCmd->set_strsize((uint32_t)(linkEditSegSize - linkeditOffsetSymbolPoolOffset));
// LC_DYSYMTAB
cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
_dynamicSymbolTableCmd->set_nmodtab(0);
_dynamicSymbolTableCmd->set_extrefsymoff(0);
_dynamicSymbolTableCmd->set_nextrefsyms(0);
- _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetIndirectSymbolTable));
+ _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetIndirectSymbolTable));
_dynamicSymbolTableCmd->set_nindirectsyms(stubCount);
_dynamicSymbolTableCmd->set_extreloff(0);
_dynamicSymbolTableCmd->set_nextrel(0);
// write stubs section content
_stubInstructions = (uint32_t*)((uint8_t*)mh + _firstStubOffset);
- for (int i=0; i < stubCount; ++i) {
+ for (unsigned i=0; i < stubCount; ++i) {
E::set32(_stubInstructions[i], 0xD4200000);
}
// write linkedit content
- uint8_t* linkeditBufferStart = (uint8_t*)cache + poolLinkEditStartOffset;
+ uint8_t* linkeditBufferStart = (uint8_t*)poolLinkEditStartAddr + cacheSlide;
// write symbol table
_symbolTable = (macho_nlist<P>*)(linkeditBufferStart);
- for (int i=0; i < stubCount; ++i) {
+ for (unsigned 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);
}
// write indirect symbol table
uint32_t* indirectSymboTable = (uint32_t*)(linkeditBufferStart + linkeditOffsetIndirectSymbolTable);
- for (int i=0; i < stubCount; ++i) {
+ for (unsigned i=0; i < stubCount; ++i) {
P::E::set32(indirectSymboTable[i], i);
}
// write string pool
_dynamicSymbolTableCmd->set_nundefsym(_nextIndex);
uint8_t digest[CC_MD5_DIGEST_LENGTH];
- CC_MD5(_stubInstructions, _maxStubs*sizeof(uint64_t), digest);
+ CC_MD5(_stubInstructions, _maxStubs*sizeof(uint32_t), digest);
_uuidCmd->set_uuid(digest);
if ( verbose ) {
template <typename P>
class StubOptimizer {
public:
- StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diags);
+ StubOptimizer(const DyldSharedCache* cache, 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(); }
+ const uint8_t* exportsTrie() { return &_linkeditBias[_dyldInfo->export_off()]; }
uint32_t exportsTrieSize() { return _dyldInfo->export_size(); }
uint32_t _stubCount = 0;
void optimizeArmCallSites();
void optimizeArmStubs();
uint64_t lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
+#if SUPPORT_ARCH_arm64e
+ uint64_t lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
+#endif
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,
macho_header<P>* _mh;
- void* _cacheBuffer;
+ int64_t _cacheSlide = 0;
+ uint64_t _cacheUnslideAddr = 0;
+ bool _chainedFixups = false;
uint32_t _linkeditSize = 0;
- uint32_t _linkeditCacheOffset = 0;
uint64_t _linkeditAddr = 0;
const uint8_t* _linkeditBias = nullptr;
const char* _installName = 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;
+ 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)
+StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags)
+: _mh(mh), _diagnostics(diags)
{
- _linkeditBias = (uint8_t*)cacheBuffer;
+ _cacheSlide = (long)cache - cache->unslidLoadAddress();
+ _cacheUnslideAddr = cache->unslidLoadAddress();
+#if SUPPORT_ARCH_arm64e
+ _chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
+#else
+ _chainedFixups = false;
+#endif
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;
segCmd =( macho_segment_command<P>*)cmd;
_segCmds.push_back(segCmd);
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditBias = (uint8_t*)(segCmd->vmaddr() + _cacheSlide - segCmd->fileoff());
_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) {
return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8;
}
+#if SUPPORT_ARCH_arm64e
+template <typename P>
+uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+ uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
+ // ADRP X17, dyld_mageLoaderCache@page
+ if ( (stubInstr1 & 0x9F00001F) != 0x90000011 ) {
+ _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;
+
+ // ADD X17, X17, dyld_mageLoaderCache@pageoff
+ uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
+ if ( (stubInstr2 & 0xFFC003FF) != 0x91000231 ) {
+ _diagnostics.warning("second instruction of stub (0x%08X) is not ADD for stub at addr 0x%0llX in %s",
+ stubInstr2, (uint64_t)stubVMAddr, _installName);
+ return 0;
+ }
+ uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10);
+
+ // LDR X16, [X17]
+ uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions + 8));
+ if ( stubInstr3 != 0xF9400230 ) {
+ _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;
+ }
+ return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue;
+}
+#endif
+
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_nlist<P>* symbolTable = (const macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+ const char* symbolStrings = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
+ const uint32_t* const indirectTable = (uint32_t*)(&_linkeditBias[_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;
switch ( symbolIndex ) {
case INDIRECT_SYMBOL_ABS:
case INDIRECT_SYMBOL_LOCAL:
+ case INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL:
break;
default:
if ( symbolIndex >= _symTabCmd->nsyms() ) {
}
const char* symName = &symbolStrings[stringOffset];
if ( neverStubEliminate.count(symName) ) {
- //verboseLog("not bypassing stub to %s in %s because target is interposable\n", symName, _installName);
+ //fprintf(stderr, "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();
+ const uint8_t* stubInstrs = (uint8_t*)(long)stubVMAddr + _cacheSlide;
pint_t targetLPAddr = 0;
switch ( _mh->cputype() ) {
case CPU_TYPE_ARM64:
- targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
+ case CPU_TYPE_ARM64_32:
+#if SUPPORT_ARCH_arm64e
+ if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64_E)
+ targetLPAddr = (pint_t)lazyPointerAddrFromArm64eStub(stubInstrs, stubVMAddr);
+ else
+#endif
+ targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
break;
case CPU_TYPE_ARM:
targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr);
}
}
}
- else if ( sectionType == S_LAZY_SYMBOL_POINTERS ) {
+ else if ( (sectionType == S_LAZY_SYMBOL_POINTERS) || (sectionType == S_NON_LAZY_SYMBOL_POINTERS) ) {
pint_t lpVMAddr;
- pint_t* lpContent = (pint_t*)(((uint8_t*)_cacheBuffer) + sect->offset());
+ pint_t* lpContent = (pint_t*)(sect->addr() + _cacheSlide);
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();
switch ( symbolIndex ) {
case INDIRECT_SYMBOL_ABS:
case INDIRECT_SYMBOL_LOCAL:
+ case INDIRECT_SYMBOL_LOCAL|INDIRECT_SYMBOL_ABS:
break;
default:
lpValue = (pint_t)P::getP(lpContent[j]);
+
+ // Fixup threaded rebase/bind
+ if ( _chainedFixups ) {
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
+ ptr.raw = lpValue;
+ assert(ptr.authRebase.bind == 0);
+ if ( ptr.authRebase.auth ) {
+ lpValue = (pint_t)(_cacheUnslideAddr + ptr.authRebase.target);
+ }
+ else {
+ lpValue = (pint_t)ptr.plainRebase.signExtendedTarget();
+ }
+ }
+
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",
}
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);
+ //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)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",
return;
}
- uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr;
+ uint8_t* textSectionContent = (uint8_t*)(_textSection->addr() + _cacheSlide);
// Whole :== <count> FromToSection+
// FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
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);
+ if ( kind > 13 ) {
+ _diagnostics.error("bad kind (%llu) value in %s\n", kind, _installName);
}
uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
uint64_t fromSectionOffset = 0;
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
+ uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
+ assert(stubInstructions[0] == 0xe59fc004);
+ stubInstructions[0] = 0xe59fc000; // ldr ip, L0
+ stubInstructions[1] = 0xe08ff00c; // add pc, pc, ip
+ stubInstructions[2] = delta; // L0: .long xxxx
+ stubInstructions[3] = 0xe7ffdefe; // trap
_stubOptimizedCount++;
}
}
if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
instruction= (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
_branchesDirectCount++;
- return true;
+ return true;
}
// find closest branch island pool between instruction and target and get island
const auto& pos3 = _targetAddrToName.find((pint_t)finalTargetAddr);
switch ( _mh->cputype() ) {
case CPU_TYPE_ARM64:
+ case CPU_TYPE_ARM64_32:
optimizeArm64CallSites(branchIslandPools);
if ( verbose ) {
_diagnostics.verbose("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s\n",
template <typename P>
void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std::vector<uint64_t>& branchPoolStartAddrs,
- const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+ uint64_t branchPoolsLinkEditStartAddr, uint64_t branchPoolsLinkEditStartFileOffset,
+ 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));
+ optimizers.push_back(new StubOptimizer<P>(cache, (macho_header<P>*)mh, diags));
});
// construct a BranchPoolDylib for each pool
});
__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;
+ ((dyld3::MachOFile*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+ if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ if ( info.fileOffset >= lastLinkEditRegionUsedOffset )
+ lastLinkEditRegionUsedOffset = info.fileOffset + info.vmSize;
}
});
});
- uint64_t allPoolsLinkEditStartOffset = lastLinkEditRegionUsedOffset;
- uint64_t allPoolsLinkEditStartAddr = linkEditRegionStartAddr + allPoolsLinkEditStartOffset - linkEditRegionStartCacheOffset;
- uint64_t allPoolsLinkEditSize = linkEditRegionEndAddr - allPoolsLinkEditStartAddr;
+ uint64_t allPoolsLinkEditStartAddr = branchPoolsLinkEditStartAddr;
if ( !branchPoolStartAddrs.empty() ) {
- uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr;
- uint64_t poolLinkEditStartOffset = allPoolsLinkEditStartOffset;
- const uint64_t poolSize = (allPoolsLinkEditSize/branchPoolStartAddrs.size()) & (-4096);
+ uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr;
+ uint64_t poolLinkEditFileOffset = branchPoolsLinkEditStartFileOffset;
+ const uint64_t poolSize = branchPoolLinkEditSize("arm64");
for (uint64_t poolAddr : branchPoolStartAddrs) {
- pools.push_back(new BranchPoolDylib<P>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset, diags));
- poolLinkEditStartAddr += poolSize;
- poolLinkEditStartOffset += poolSize;
+ pools.push_back(new BranchPoolDylib<P>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditFileOffset, diags));
+ poolLinkEditStartAddr += poolSize;
+ poolLinkEditFileOffset += poolSize;
}
}
}
}
-void bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+void CacheBuilder::optimizeAwayStubs(const std::vector<uint64_t>& branchPoolStartAddrs, uint64_t branchPoolsLinkEditStartAddr)
{
- std::string archName = cache->archName();
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ uint64_t branchPoolsLinkEditStartFileOffset = _readOnlyRegion.cacheFileOffset + branchPoolsLinkEditStartAddr - _readOnlyRegion.unslidLoadAddress;
+ std::string archName = dyldCache->archName();
+#if SUPPORT_ARCH_arm64_32
+ if ( startsWith(archName, "arm64_32") )
+ bypassStubs<Pointer32<LittleEndian> >(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
+ else
+#endif
if ( startsWith(archName, "arm64") )
- bypassStubs<Pointer64<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+ bypassStubs<Pointer64<LittleEndian> >(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
else if ( archName == "armv7k" )
- bypassStubs<Pointer32<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+ bypassStubs<Pointer32<LittleEndian>>(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
// no stub optimization done for other arches
}
#include "Trie.hpp"
#include "DyldSharedCache.h"
#include "CacheBuilder.h"
-
+#include "MachOLoaded.h"
#define ALIGN_AS_TYPE(value, type) \
((value + alignof(type) - 1) & (-alignof(type)))
// 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) {
+ for (auto& entry : _map) {
+ const std::string& symName = entry.first;
// 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]) {
+ for (uint32_t symbolIndex : entry.second) {
symbolTable[symbolIndex].set_n_strx(poolOffset);
}
poolOffset += symName.size() + 1;
private:
- std::unordered_map<std::string, std::vector<uint32_t>> _map;
+ std::map<std::string, std::vector<uint32_t>> _map;
};
+} // anonymous namespace
struct LocalSymbolInfo
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);
const std::vector<macho_segment_command<P>*>& segCmds() { return _segCmds; }
+ static void optimizeLinkedit(CacheBuilder& builder);
+ static void mergeLinkedits(CacheBuilder& builder, std::vector<LinkeditOptimizer<P>*>& optimizers);
+
private:
typedef typename P::uint_t pint_t;
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;
}
}
+
// 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];
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;
+ uint64_t textSegAddr = 0;
+ int64_t slide = 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;
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();
+ if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
+ textSegAddr = segCmd->vmaddr();
+ slide = (uint64_t)mh - textSegAddr;
+ }
+ else if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
_linkeditAddr = segCmd->vmaddr();
+ _linkeditBias = (uint8_t*)mh + (_linkeditAddr - textSegAddr) - segCmd->fileoff();
+ _linkeditSize = (uint32_t)segCmd->vmsize();
}
else if ( segCmd->nsects() > 0 ) {
macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
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 pint_t* inits = (pint_t*)(sect->addr()+slide);
const size_t count = sect->size() / sizeof(pint_t);
for (size_t j=0; j < count; ++j) {
uint64_t func = P::getP(inits[j]);
_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) {
+ for (uint32_t 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);
}
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)
+void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, 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;
+ uint64_t totalUnoptLinkeditsSize = builder._readOnlyRegion.sizeInUse - builder._nonLinkEditReadOnlySize;
uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
SortedStringPool<P> stringPool;
uint32_t offset = 0;
- diagnostics.verbose("Merged LINKEDIT:\n");
+ builder._diagnostics.verbose("Merged LINKEDIT:\n");
// copy weak binding info
uint32_t startWeakBindInfosOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
- op->copyWeakBindingInfo(newLinkEdit, offset);
+ // Skip chained fixups as the in-place linked list isn't valid any more
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+ if (!mf->hasChainedFixups())
+ op->copyWeakBindingInfo(newLinkEdit, offset);
}
- diagnostics.verbose(" weak bindings size: %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
+ builder._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);
+ builder._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);
+ // Skip chained fixups as the in-place linked list isn't valid any more
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+ if (!mf->hasChainedFixups())
+ op->copyBindingInfo(newLinkEdit, offset);
}
- diagnostics.verbose(" bindings size: %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
+ builder._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);
+ // Skip chained fixups as the in-place linked list isn't valid any more
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+ if (!mf->hasChainedFixups())
+ op->copyLazyBindingInfo(newLinkEdit, offset);
}
- diagnostics.verbose(" lazy bindings size: %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
+ builder._diagnostics.verbose(" lazy bindings size: %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
}
// copy symbol table entries
std::vector<macho_nlist<P>> unmappedLocalSymbols;
- if ( dontMapLocalSymbols )
+ if ( builder._options.excludeLocalSymbols )
unmappedLocalSymbols.reserve(0x01000000);
std::vector<LocalSymbolInfo> localSymbolInfos;
localSymbolInfos.reserve(optimizers.size());
uint32_t sharedSymbolTableExportsCount = 0;
uint32_t sharedSymbolTableImportsCount = 0;
for (LinkeditOptimizer<P>* op : optimizers) {
- op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
+ op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, builder._options.excludeLocalSymbols,
localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
uint32_t x = symbolIndex;
op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyFunctionStarts(newLinkEdit, offset);
}
- diagnostics.verbose(" function starts size: %5uKB\n", (offset-startFunctionStartsOffset)/1024);
+ builder._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);
+ builder._diagnostics.verbose(" data in code size: %5uKB\n", (offset-startDataInCodeOffset)/1024);
// copy indirect symbol tables
for (LinkeditOptimizer<P>* op : optimizers) {
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);
+ uint64_t newLinkeditAlignedSize = align(offset, 14);
+ builder._diagnostics.verbose(" symbol table size: %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+ builder._diagnostics.verbose(" symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
+ builder._sharedStringsPoolVmOffset = (uint32_t)((builder._readOnlyRegion.unslidLoadAddress - builder._readExecuteRegion.unslidLoadAddress) + builder._nonLinkEditReadOnlySize + sharedSymbolStringsOffset);
// 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);
+ builder._diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+ ::memcpy(builder._readOnlyRegion.buffer+builder._nonLinkEditReadOnlySize, newLinkEdit, newLinkeditAlignedSize);
::free(newLinkEdit);
+ builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
// If making cache for customers, add extra accelerator tables for dyld
- if ( addAcceleratorTables ) {
- AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
+ DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
+ if ( builder._options.optimizeStubs ) {
+ uint64_t addrWhereAccTablesWillBe = builder._readOnlyRegion.unslidLoadAddress+builder._readOnlyRegion.sizeInUse;
+ uint64_t addrWhereMergedLinkWillStart = builder._readOnlyRegion.unslidLoadAddress+builder._nonLinkEditReadOnlySize;
+ AcceleratorTables<P> tables(cacheHeader, addrWhereMergedLinkWillStart, builder._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));
+ if ( tablesSize < (builder._readOnlyRegion.bufferSize - builder._readOnlyRegion.sizeInUse) ) {
+ tables.copyTo(builder._readOnlyRegion.buffer+builder._readOnlyRegion.sizeInUse);
+ cacheHeader->header.accelerateInfoAddr = addrWhereAccTablesWillBe;
+ cacheHeader->header.accelerateInfoSize = tablesSize;
+ builder._readOnlyRegion.sizeInUse += align(tablesSize, 14);
+ builder._diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
}
else {
- diagnostics.warning("not enough room to add dyld accelerator tables");
+ builder._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;
+ if ( builder._options.excludeLocalSymbols ) {
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 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;
+ vm_address_t localsBuffer;
+ if ( ::vm_allocate(mach_task_self(), &localsBuffer, localsBufferSize, VM_FLAGS_ANYWHERE) == 0 ) {
+ dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)localsBuffer;
+ // 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 (uint32_t 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>*)(localsBuffer+nlistOffset);
+ ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
+ // copy string pool
+ localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+ // update cache header
+ cacheHeader->header.localSymbolsSize = localsBufferSize;
+ // return buffer of local symbols, caller to free() it
+ builder._localSymbolsRegion.buffer = (uint8_t*)localsBuffer;
+ builder._localSymbolsRegion.bufferSize = localsBufferSize;
+ builder._localSymbolsRegion.sizeInUse = localsBufferSize;
+ }
+ else {
+ builder._diagnostics.warning("could not allocate local symbols");
}
- // 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
+ uint64_t linkeditsUnslidStartAddr = builder._readOnlyRegion.unslidLoadAddress + builder._nonLinkEditReadOnlySize;
+ uint32_t linkeditsCacheFileOffset = (uint32_t)(builder._readOnlyRegion.cacheFileOffset + builder._nonLinkEditReadOnlySize);
for (LinkeditOptimizer<P>* op : optimizers) {
- op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
+ op->updateLoadCommands(linkeditsCacheFileOffset, linkeditsUnslidStartAddr, newLinkeditUnalignedSize,
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)
+void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder)
{
+ DyldSharedCache* cache = (DyldSharedCache*)builder._readExecuteRegion.buffer;
// 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));
+ optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, builder._diagnostics));
});
#if 0
// add optimizer for each branch pool
}
#endif
// merge linkedit info
- uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
+ mergeLinkedits(builder, optimizers);
// 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)
+void CacheBuilder::optimizeLinkedit(const std::vector<uint64_t>& branchPoolOffsets)
{
- if ( is64) {
- return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+ if ( _archLayout->is64 ) {
+ return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
}
else {
- return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+ return LinkeditOptimizer<Pointer32<LittleEndian>>::optimizeLinkedit(*this);
}
}
#include "CacheBuilder.h"
#include "FileAbstraction.hpp"
#include "MachOFileAbstraction.hpp"
-
+#include "MachOLoaded.h"
// Scan a C++ or Swift length-mangled field.
static bool scanMangledField(const char *&string, const char *end,
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 };
- });
+ _cacheStart = (uint8_t*)cache;
+ _cacheUnslideAddr = cache->unslidLoadAddress();
+ _slide = (uint64_t)cache - _cacheUnslideAddr;
+#if SUPPORT_ARCH_arm64e
+ _chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
+#else
+ _chainedFixups = false;
+#endif
+ }
+
+ // Converts from an on disk vmAddr to the real vmAddr
+ // That is, for a chained fixup, decodes the chain, for a non-chained fixup, does nothing.
+ uint64_t vmAddrForOnDiskVMAddr(uint64_t vmaddr) {
+ if ( _chainedFixups ) {
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
+ ptr.raw = vmaddr;
+ assert(ptr.authRebase.bind == 0);
+ if ( ptr.authRebase.auth ) {
+ vmaddr = _cacheUnslideAddr + ptr.authRebase.target;
+ }
+ else {
+ vmaddr = ptr.plainRebase.signExtendedTarget();
+ }
+ }
+ return vmaddr;
}
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;
+ vmaddr = vmAddrForOnDiskVMAddr(vmaddr);
+ if ( vmaddr != 0 ) {
+ uint64_t offset = vmaddr - _cacheUnslideAddr;
+ return _cacheStart + offset;
+ } else
+ 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;
+ if ( content != nullptr )
+ return _cacheUnslideAddr + ((uint8_t*)content - _cacheStart);
+ else
+ 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];
+ Diagnostics& _diagnostics;
+ uint64_t _slide;
+ uint64_t _cacheUnslideAddr;
+ uint8_t* _cacheStart;
+ bool _chainedFixups;
};
return (pint_t)P::getP(_base[index]);
}
+ pint_t getSectionVMAddress() const {
+ return (pint_t)_section->addr();
+ }
+
T get(pint_t index) const {
return (T)_cache->contentForVMAddr(getVMAddress(index));
}
{
_count++;
const char *s = (const char *)_cache->contentForVMAddr(oldValue);
+ oldValue = (pint_t)_cache->vmAddrForOnDiskVMAddr(oldValue);
objc_opt::string_map::iterator element =
_selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
return (pint_t)element->second;
const char *writeProtocols(ContentAccessor* cache,
uint8_t *& rwdest, size_t& rwremaining,
uint8_t *& rodest, size_t& roremaining,
- std::vector<void*>& pointersInData,
+ CacheBuilder::ASLR_Tracker& aslrTracker,
pint_t protocolClassVMAddr)
{
if (_protocolCount == 0) return NULL;
iter->second = cache->vmAddrForContent(proto);
// Add new rebase entries.
- proto->addPointers(pointersInData);
+ proto->addPointers(cache, aslrTracker);
}
return NULL;
template <typename P>
-void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::ASLR_Tracker& aslrTracker,
+ CacheBuilder::LOH_Tracker& lohTracker,
+ const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag)
{
typedef typename P::E E;
typedef typename P::uint_t pint_t;
}
else {
for (const macho_header<P>* mh : addressSortedDylibs) {
- hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+ hinfoROOptimizer.update(&cacheAccessor, mh, aslrTracker);
}
}
}
else {
for (const macho_header<P>* mh : addressSortedDylibs) {
- hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+ hinfoRWOptimizer.update(&cacheAccessor, mh, aslrTracker);
}
}
if (forProduction) {
WeakClassDetector<P> weakopt;
noMissingWeakSuperclasses =
- weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs);
+ weakopt.noMissingWeakSuperclasses(&cacheAccessor, missingWeakImports, sizeSortedDylibs);
// Shared cache does not currently support unbound weak references.
// Here we assert that there are none. If support is added later then
err = protocolOptimizer.writeProtocols(&cacheAccessor,
optRWData, optRWRemaining,
optROData, optRORemaining,
- pointersForASLR, protocolClassVMAddr);
+ aslrTracker, protocolClassVMAddr);
if (err) {
diag.warning("%s", err);
return;
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);
+
+ // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD
+ if (forProduction) {
+ uint64_t lohADRPCount = 0;
+ uint64_t lohLDRCount = 0;
+
+ for (auto& targetAndInstructions : lohTracker) {
+ uint64_t targetVMAddr = targetAndInstructions.first;
+ if (!selOptimizer.isSelectorRefAddress((pint_t)targetVMAddr))
+ continue;
+
+ std::set<void*>& instructions = targetAndInstructions.second;
+ // We do 2 passes over the instructions. The first to validate them and the second
+ // to actually update them.
+ for (unsigned pass = 0; pass != 2; ++pass) {
+ uint32_t adrpCount = 0;
+ uint32_t ldrCount = 0;
+ for (void* instructionAddress : instructions) {
+ uint32_t& instruction = *(uint32_t*)instructionAddress;
+ uint64_t instructionVMAddr = cacheAccessor.vmAddrForContent(&instruction);
+ uint64_t selRefContent = *(uint64_t*)cacheAccessor.contentForVMAddr(targetVMAddr);
+ const char* selectorString = (const char*)cacheAccessor.contentForVMAddr(selRefContent);
+ uint64_t selectorStringVMAddr = cacheAccessor.vmAddrForContent(selectorString);
+
+ if ( (instruction & 0x9F000000) == 0x90000000 ) {
+ // ADRP
+ int64_t pageDistance = ((selectorStringVMAddr & ~0xFFF) - (instructionVMAddr & ~0xFFF));
+ int64_t newPage21 = pageDistance >> 12;
+
+ if (pass == 0) {
+ if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) {
+ diag.verbose("Out of bounds ADRP selector reference target\n");
+ instructions.clear();
+ break;
+ }
+ ++adrpCount;
+ }
+
+ if (pass == 1) {
+ instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
+ ++lohADRPCount;
+ }
+ continue;
+ }
+
+ if ( (instruction & 0x3B000000) == 0x39000000 ) {
+ // LDR/STR. STR shouldn't be possible as this is a selref!
+ if (pass == 0) {
+ if ( (instruction & 0xC0C00000) != 0xC0400000 ) {
+ // Not a load, or dest reg isn't xN, or uses sign extension
+ diag.verbose("Bad LDR for selector reference optimisation\n");
+ instructions.clear();
+ break;
+ }
+ if ( (instruction & 0x04000000) != 0 ) {
+ // Loading a float
+ diag.verbose("Bad LDR for selector reference optimisation\n");
+ instructions.clear();
+ break;
+ }
+ ++ldrCount;
+ }
+
+ if (pass == 1) {
+ uint32_t ldrDestReg = (instruction & 0x1F);
+ uint32_t ldrBaseReg = ((instruction >> 5) & 0x1F);
+
+ // Convert the LDR to an ADD
+ instruction = 0x91000000;
+ instruction |= ldrDestReg;
+ instruction |= ldrBaseReg << 5;
+ instruction |= (selectorStringVMAddr & 0xFFF) << 10;
+
+ ++lohLDRCount;
+ }
+ continue;
+ }
+
+ if ( (instruction & 0xFFC00000) == 0x91000000 ) {
+ // ADD imm12
+ // We don't support ADDs.
+ diag.verbose("Bad ADD for selector reference optimisation\n");
+ instructions.clear();
+ break;
+ }
+
+ diag.verbose("Unknown instruction for selref optimisation\n");
+ instructions.clear();
+ break;
+ }
+ if (pass == 0) {
+ // If we didn't see at least one ADRP/LDR in pass one then don't optimize this location
+ if ((adrpCount == 0) || (ldrCount == 0)) {
+ instructions.clear();
+ break;
+ }
+ }
+ }
+ }
+
+ diag.verbose(" Optimized %lld ADRP LOHs\n", lohADRPCount);
+ diag.verbose(" Optimized %lld LDR LOHs\n", lohLDRCount);
+ }
}
} // anon namespace
-void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void CacheBuilder::optimizeObjC()
{
- if ( is64 )
- optimizeObjC<Pointer64<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+ if ( _archLayout->is64 )
+ doOptimizeObjC<Pointer64<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, _missingWeakImports, _diagnostics);
else
- optimizeObjC<Pointer32<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+ doOptimizeObjC<Pointer32<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, _missingWeakImports, _diagnostics);
}
return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
}
+inline bool startsWith(const std::string_view& str, const std::string_view& 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());
inline void bytesToHex(const uint8_t* bytes, size_t byteCount, char buffer[])
{
char* p = buffer;
- for (int i=0; i < byteCount; ++i) {
+ for (size_t i=0; i < byteCount; ++i) {
*p++ = hexDigit(bytes[i] >> 4);
*p++ = hexDigit(bytes[i] & 0x0F);
}
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
+ uint32_t formatVersion : 8, // dyld3::closure::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
+ locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache
+ padding : 21; // TBD
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
+ uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache
+ uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache
+ uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs
+ uint64_t dylibsTrieSize; // size of trie of cached dylib paths
+ uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
+ uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures
};
//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
+#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
+
+
+
+// The version 3 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
+//
+// There are two cases:
+//
+// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
+// The page contains no values that need rebasing.
+//
+// 2) otherwise...
+// All rebase locations are in one linked list. The offset of the first
+// rebase location in the page is pageStarts[pageIndex].
+//
+// A pointer is one of of the variants in dyld_cache_slide_pointer3
+//
+// The code for processing a linked list (chain) is:
+//
+// uint32_t delta = pageStarts[pageIndex];
+// dyld_cache_slide_pointer3* loc = pageStart;
+// do {
+// loc += delta;
+// delta = loc->offsetToNextPointer;
+// if ( loc->auth.authenticated ) {
+// newValue = loc->offsetFromSharedCacheBase + results->slide + auth_value_add;
+// newValue = sign_using_the_various_bits(newValue);
+// }
+// else {
+// uint64_t value51 = loc->pointerValue;
+// uint64_t top8Bits = value51 & 0x0007F80000000000ULL;
+// uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
+// uint64_t targetValue = ( top8Bits << 13 ) | bottom43Bits;
+// newValue = targetValue + results->slide;
+// }
+// loc->raw = newValue;
+// } while (delta != 0);
+//
+//
+struct dyld_cache_slide_info3
+{
+ uint32_t version; // currently 3
+ uint32_t page_size; // currently 4096 (may also be 16384)
+ uint32_t page_starts_count;
+ uint64_t auth_value_add;
+ uint16_t page_starts[/* page_starts_count */];
+};
+
+#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
+
+union dyld_cache_slide_pointer3
+{
+ uint64_t raw;
+ struct {
+ uint64_t pointerValue : 51,
+ offsetToNextPointer : 11,
+ unused : 2;
+ } plain;
+
+ struct {
+ uint64_t offsetFromSharedCacheBase : 32,
+ diversityData : 16,
+ hasAddressDiversity : 1,
+ key : 2,
+ offsetToNextPointer : 11,
+ unused : 1,
+ authenticated : 1; // = 1;
+ } auth;
+};
+
+
+
+// The version 4 of the slide info is optimized for 32-bit caches up to 1GB.
+// Since only interior pointers (pointers that point within the cache) are rebased
+// (slid), we know the possible range of the pointers takes 30 bits. That
+// gives us two bits to use to chain to the next rebase.
+//
+// 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_SLIDE4_PAGE_NO_REBASE
+// The page contains no values that need rebasing.
+//
+// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_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_SLIDE4_PAGE_USE_EXTRA
+// Multiple chains 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 chain in the page. The first is at:
+// extrasStartIndex = (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_INDEX)
+// The next is at extrasStartIndex+1. The last is denoted by
+// having the high bit (DYLD_CACHE_SLIDE4_PAGE_EXTRA_END) of the pageExtras[].
+//
+// For 32-bit architectures, there are only two bits free (the two 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 12 bytes.
+// To reduce the number or chains needed, an optimization was added. Turns
+// most of the non-rebased data are small values and can be co-opt'ed into
+// being used in the chain. The can be done because nothing
+// in the shared cache should point to the first 64KB which are in the shared
+// cache header information. So if the resulting pointer points to the
+// start of the cache +/-32KB, then it is actually a small number that should
+// not be rebased, but just reconstituted.
+//
+// The code for processing a linked list (chain) is:
+//
+// uint32_t delta = 1;
+// while ( delta != 0 ) {
+// uint8_t* loc = pageStart + pageOffset;
+// uint32_t rawValue = *((uint32_t*)loc);
+// delta = ((rawValue & deltaMask) >> deltaShift);
+// uintptr_t newValue = (rawValue & valueMask);
+// if ( (newValue & 0xFFFF8000) == 0 ) {
+// // small positive non-pointer, use as-is
+// }
+// else if ( (newValue & 0x3FFF8000) == 0x3FFF8000 ) {
+// // small negative non-pointer
+// newValue |= 0xC0000000;
+// }
+// else {
+// // pointer that needs rebasing
+// newValue += valueAdd;
+// newValue += slideAmount;
+// }
+// *((uint32_t*)loc) = newValue;
+// pageOffset += delta;
+// }
+//
+//
+struct dyld_cache_slide_info4
+{
+ uint32_t version; // currently 4
+ 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 (0xC0000000)
+ uint64_t value_add; // base address of cache
+ //uint16_t page_starts[page_starts_count];
+ //uint16_t page_extras[page_extras_count];
+};
+#define DYLD_CACHE_SLIDE4_PAGE_NO_REBASE 0xFFFF // page has no rebasing
+#define DYLD_CACHE_SLIDE4_PAGE_INDEX 0x7FFF // mask of page_starts[] values
+#define DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA 0x8000 // index is into extras array (not a chain start offset)
+#define DYLD_CACHE_SLIDE4_PAGE_EXTRA_END 0x8000 // last chain entry for page
+
+
struct dyld_cache_local_symbols_info
#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"
+#include "ClosureBuilder.h"
+#include "ClosurePrinter.h"
+#include "ClosureFileSystemPhysical.h"
+
+using dyld3::closure::ImageArray;
+using dyld3::closure::Image;
+using dyld3::closure::ImageNum;
+using dyld3::closure::ClosureBuilder;
+using dyld3::closure::LaunchClosure;
+using dyld3::closure::DlopenClosure;
+using dyld3::closure::PathOverrides;
+using dyld3::Array;
-extern "C" {
- #include "closuredProtocol.h"
-}
+// mmap() an shared cache file read/only but laid out like it would be at runtime
static const DyldSharedCache* mapCacheFile(const char* path)
{
struct stat statbuf;
- if (stat(path, &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);
+ 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;
+ uint8_t firstPage[4096];
+ if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
+ fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
+ return nullptr;
}
- 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);
+ const dyld_cache_header* header = (dyld_cache_header*)firstPage;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
+
+ size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
+ vm_address_t result;
+ kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
+ if ( r != KERN_SUCCESS ) {
+ fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
+ return nullptr;
+ }
+ for (int i=0; i < 3; ++i) {
+ void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
+ PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+ 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);
- //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;
+ return (DyldSharedCache*)result;
}
-*/
static void usage()
{
- printf("dyld_closure_util program to create of view dyld3 closures\n");
+ printf("dyld_closure_util program to create or 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(" -list_dyld_cache_closures # list all launch closures in the dyld shared cache with size\n");
+ printf(" -list_dyld_cache_dlopen_closures # list all dlopen closures in the dyld shared cache with size\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(" -print_dyld_cache_dylib <dylib-path> # print specified cached dylib as JSON\n");
+ printf(" -print_dyld_cache_dylibs # print all cached dylibs as JSON\n");
+ printf(" -print_dyld_cache_dlopen <path> # print specified dlopen closure as JSON\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(" -dlopen <path> # for use with -create_closure to simulate that program calling dlopen\n");
printf(" -verbose_fixups # for use with -print* options to force printing fixups\n");
+ printf(" -no_at_paths # when building a closure, simulate security not allowing @path expansion\n");
+ printf(" -no_fallback_paths # when building a closure, simulate security not allowing default fallback paths\n");
+ printf(" -allow_insertion_failures # when building a closure, simulate security allowing unloadable DYLD_INSERT_LIBRARIES to be ignored\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 listCacheDlopenClosures = false;
bool printCachedDylibs = false;
- bool printOtherDylibs = false;
- bool printPatchTable = false;
- bool useClosured = false;
bool verboseFixups = false;
+ bool allowAtPaths = true;
+ bool allowFallbackPaths = true;
+ bool allowInsertionFailures = false;
std::vector<std::string> buildtimePrefixes;
- std::vector<std::string> envArgs;
+ std::vector<const char*> envArgs;
std::vector<const char*> dlopens;
if ( argc == 1 ) {
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 ) {
+ 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");
}
dlopens.push_back(path);
}
- else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
+ else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
verboseFixups = true;
}
+ else if ( strcmp(arg, "-no_at_paths") == 0 ) {
+ allowAtPaths = false;
+ }
+ else if ( strcmp(arg, "-no_fallback_paths") == 0 ) {
+ allowFallbackPaths = false;
+ }
+ else if ( strcmp(arg, "-allow_insertion_failures") == 0 ) {
+ allowInsertionFailures = true;
+ }
else if ( strcmp(arg, "-build_root") == 0 ) {
const char* buildRootPath = argv[++i];
if ( buildRootPath == nullptr ) {
}
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, "-list_dyld_cache_dlopen_closures") == 0 ) {
+ listCacheDlopenClosures = true;
}
else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
printCacheClosure = argv[++i];
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 ) {
return 1;
}
}
- else if ( strcmp(arg, "-print_dyld_cache_other") == 0 ) {
+ else if ( strcmp(arg, "-print_dyld_cache_dlopen") == 0 ) {
printOtherDylib = argv[++i];
if ( printOtherDylib == nullptr ) {
- fprintf(stderr, "-print_dyld_cache_other option requires a path \n");
+ fprintf(stderr, "-print_dyld_cache_dlopen 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) ) {
}
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;
- }
+ envArgs.push_back(nullptr);
+
const DyldSharedCache* dyldCache = nullptr;
- bool dyldCacheIsRaw = false;
+ bool dyldCacheIsLive = true;
if ( cacheFilePath != nullptr ) {
dyldCache = mapCacheFile(cacheFilePath);
- dyldCacheIsRaw = true;
+ dyldCacheIsLive = false;
}
else {
-#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
+ fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
+ return 1;
+#else
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("");
+ dyld3::Platform platform = dyldCache->platform();
+ const char* archName = dyldCache->archName();
- 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());
+ PathOverrides pathOverrides;
+ pathOverrides.setFallbackPathHandling(allowFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::none);
+ pathOverrides.setEnvVars(&envArgs[0], nullptr, nullptr);
+ const char* prefix = ( buildtimePrefixes.empty() ? nullptr : buildtimePrefixes.front().c_str());
+ //dyld3::PathOverrides pathStuff(envArgs);
+ STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3+dlopens.size());
+ STACK_ALLOC_ARRAY(dyld3::LoadedImage, loadedArray, 1024);
+ imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+ imagesArrays.push_back(dyldCache->otherOSImageArray());
+
+ dyld3::closure::FileSystemPhysical fileSystem(prefix);
+ ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
+ ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHanding, nullptr, archName, platform, nullptr);
+ const LaunchClosure* mainClosure = builder.makeLaunchClosure(inputMainExecutablePath, allowInsertionFailures);
+ if ( builder.diagnostics().hasError() ) {
+ fprintf(stderr, "dyld_closure_util: %s\n", builder.diagnostics().errorMessage());
return 1;
}
- for (const std::string& warn : closureDiag.warnings() )
- fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+ ImageNum nextNum = builder.nextFreeImageNum();
- 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);
+ if ( !dlopens.empty() )
+ printf("[\n");
+ imagesArrays.push_back(mainClosure->images());
+ dyld3::closure::printClosureAsJSON(mainClosure, imagesArrays, verboseFixups);
+ ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, mainClosure);
- 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");
- }
+ for (const char* path : dlopens) {
+ 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;
+ // map unloaded mach-o files for use during closure building
+ for (dyld3::LoadedImage& li : loadedArray) {
+ if ( li.loadedAddress() != nullptr )
+ continue;
+ if ( li.image()->inDyldCache() ) {
+ li.setLoadedAddress((dyld3::MachOLoaded*)((uint8_t*)dyldCache + li.image()->cacheOffset()));
+ }
+ else {
+ Diagnostics diag;
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archName, platform);
+ li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
+ }
}
- }
- 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);
+ ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
+ ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHandingDlopen, nullptr, archName, platform, nullptr);
+ ImageNum topImageNum;
+ const DlopenClosure* dlopenClosure = dlopenBuilder.makeDlopenClosure(path, mainClosure, loadedArray, 0, false, false, &topImageNum);
+ if ( dlopenBuilder.diagnostics().hasError() ) {
+ fprintf(stderr, "dyld_closure_util: %s\n", dlopenBuilder.diagnostics().errorMessage());
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();
+ if ( dlopenClosure == nullptr ) {
+ if ( topImageNum == 0 ) {
+ fprintf(stderr, "dyld_closure_util: failed to dlopen %s\n", path);
+ return 1;
+ }
+ printf("{\n \"dyld-cache-image-num\": \"0x%04X\"\n}\n", topImageNum);
+ }
+ else {
+ nextNum += dlopenClosure->images()->imageCount();
+ imagesArrays.push_back(dlopenClosure->images());
+ dyld3::closure::printClosureAsJSON(dlopenClosure, imagesArrays, verboseFixups);
+ ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, dlopenClosure);
}
}
- 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);
+ if ( !dlopens.empty() )
+ printf("]\n");
}
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);
+ dyldCache->forEachLaunchClosure(^(const char* runtimePath, const dyld3::closure::LaunchClosure* closure) {
+ 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 ( listCacheDlopenClosures ) {
+ dyldCache->forEachDlopenImage(^(const char* runtimePath, const dyld3::closure::Image* image) {
+ printf("%6lu %s\n", image->size(), runtimePath);
+ });
}
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);
+ const dyld3::closure::LaunchClosure* closure = dyldCache->findClosure(printCacheClosure);
+ if ( closure != nullptr ) {
+ STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
+ imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+ imagesArrays.push_back(dyldCache->otherOSImageArray());
+ imagesArrays.push_back(closure->images());
+ dyld3::closure::printClosureAsJSON(closure, imagesArrays, 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);
+ dyld3::closure::printDyldCacheImagesAsJSON(dyldCache, 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);
+ const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
+ STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+ imagesArrays.push_back(dylibs);
+ ImageNum num;
+ if ( dylibs->hasPath(printCachedDylib, num) ) {
+ dyld3::closure::printImageAsJSON(dylibs->imageForNum(num), imagesArrays, verboseFixups);
}
else {
- fprintf(stderr, "no such other image found\n");
+ fprintf(stderr, "no such 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);
+ if ( const dyld3::closure::Image* image = dyldCache->findDlopenOtherImage(printOtherDylib) ) {
+ STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+ imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+ imagesArrays.push_back(dyldCache->otherOSImageArray());
+ dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
}
else {
- fprintf(stderr, "no such other image found\n");
+ fprintf(stderr, "no such 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;
}
#include "DyldSharedCache.h"
#include "BuilderUtils.h"
#include "FileUtils.h"
+#include "JSONWriter.h"
#include "StringUtils.h"
-#include "MachOParser.h"
+#include "mrm_shared_cache_builder.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
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;
return res;
}
-void processRoots(Diagnostics& diags, std::set<std::string>& roots)
+void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *tempRootsDir)
{
std::set<std::string> processedRoots;
struct stat sb;
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")) {
+ processedRoots.insert(root);
+ continue;
+ }
+
+ char tempRootDir[MAXPATHLEN];
+ strlcpy(tempRootDir, tempRootsDir, MAXPATHLEN);
+ strlcat(tempRootDir, "/XXXXXXXX", MAXPATHLEN);
+ mkdtemp(tempRootDir);
+
+ 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();
bool writeRootList(const std::string& dstRoot, const std::set<std::string>& roots)
{
+ mkpath_np(dstRoot.c_str(), 0755);
if (roots.size() == 0)
return false;
return true;
}
-std::set<std::string> cachePaths;
-
-BOMCopierCopyOperation filteredCopy(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
+BOMCopierCopyOperation filteredCopyExcludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
{
std::string absolutePath = &path[1];
- if (cachePaths.count(absolutePath)) {
+ void *userData = BOMCopierUserData(copier);
+ std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
+ if (cachePaths->count(absolutePath)) {
return BOMCopierSkipFile;
}
return BOMCopierContinue;
}
+BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
+{
+ std::string absolutePath = &path[1];
+ void *userData = BOMCopierUserData(copier);
+ std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
+ for (const std::string& cachePath : *cachePaths) {
+ if (startsWith(cachePath, absolutePath))
+ return BOMCopierContinue;
+ }
+ if (cachePaths->count(absolutePath)) {
+ return BOMCopierContinue;
+ }
+ return BOMCopierSkipFile;
+}
+
+static std::string dispositionToString(Disposition disposition) {
+ switch (disposition) {
+ case Unknown:
+ return "Unknown";
+ case InternalDevelopment:
+ return "InternalDevelopment";
+ case Customer:
+ return "Customer";
+ case InternalMinDevelopment:
+ return "InternalMinDevelopment";
+ }
+}
+
+static std::string platformToString(Platform platform) {
+ switch (platform) {
+ case unknown:
+ return "unknown";
+ case macOS:
+ return "macOS";
+ case iOS:
+ return "iOS";
+ case tvOS:
+ return "tvOS";
+ case watchOS:
+ return "watchOS";
+ case bridgeOS:
+ return "bridgeOS";
+ case iOSMac:
+ return "iOSMac";
+ case iOS_simulator:
+ return "iOS_simulator";
+ case tvOS_simulator:
+ return "tvOS_simulator";
+ case watchOS_simulator:
+ return "watchOS_simulator";
+ }
+}
+
+static dyld3::json::Node getBuildOptionsNode(BuildOptions_v1 buildOptions) {
+ dyld3::json::Node buildOptionsNode;
+ buildOptionsNode.map["version"].value = dyld3::json::decimal(buildOptions.version);
+ buildOptionsNode.map["updateName"].value = buildOptions.updateName;
+ buildOptionsNode.map["deviceName"].value = buildOptions.deviceName;
+ buildOptionsNode.map["disposition"].value = dispositionToString(buildOptions.disposition);
+ buildOptionsNode.map["platform"].value = platformToString(buildOptions.platform);
+ for (unsigned i = 0; i != buildOptions.numArchs; ++i) {
+ dyld3::json::Node archNode;
+ archNode.value = buildOptions.archs[i];
+ buildOptionsNode.map["archs"].array.push_back(archNode);
+ }
+ buildOptionsNode.map["verboseDiagnostics"].value = buildOptions.verboseDiagnostics ? "true" : "false";
+ return buildOptionsNode;
+}
+
int main(int argc, const char* argv[])
{
@autoreleasepool {
__block Diagnostics diags;
std::set<std::string> roots;
std::string dylibCacheDir;
+ std::string artifactDir;
std::string release;
bool emitDevCaches = true;
bool emitElidedDylibs = true;
bool listConfigs = false;
bool copyRoots = false;
bool debug = false;
+ bool useMRM = false;
std::string dstRoot;
+ std::string emitJSONPath;
std::string configuration;
std::string resultPath;
+ std::string baselineDifferenceResultPath;
+ bool baselineCopyRoots = false;
+ char* tempRootsDir = strdup("/tmp/dyld_shared_cache_builder.XXXXXX");
- tempRootDir = strdup(tempRootDirTemplate);
- mkdtemp(tempRootDir);
+ mkdtemp(tempRootsDir);
for (int i = 1; i < argc; ++i) {
const char* arg = argv[i];
copyRoots = true;
} else if (strcmp(arg, "-dylib_cache") == 0) {
dylibCacheDir = realPath(argv[++i]);
+ } else if (strcmp(arg, "-artifact") == 0) {
+ artifactDir = realPath(argv[++i]);
} else if (strcmp(arg, "-no_development_cache") == 0) {
emitDevCaches = false;
} else if (strcmp(arg, "-no_overflow_dylibs") == 0) {
emitDevCaches = true;
} else if (strcmp(arg, "-overflow_dylibs") == 0) {
emitElidedDylibs = true;
+ } else if (strcmp(arg, "-mrm") == 0) {
+ useMRM = true;
+ } else if (strcmp(arg, "-emit_json") == 0) {
+ emitJSONPath = realPath(argv[++i]);
} 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 if (strcmp(arg, "-baseline_diff_results") == 0) {
+ baselineDifferenceResultPath = realPath(argv[++i]);
+ } else if (strcmp(arg, "-baseline_copy_roots") == 0) {
+ baselineCopyRoots = true;
} else {
//usage();
- diags.error("unknown option: %s\n", arg);
+ fprintf(stderr, "unknown option: %s\n", arg);
+ exit(-1);
}
} else {
if (!configuration.empty()) {
- diags.error("You may only specify one configuration");
+ fprintf(stderr, "You may only specify one configuration\n");
+ exit(-1);
}
configuration = argv[i];
}
time_t mytime = time(0);
fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
- processRoots(diags, roots);
+ writeRootList(dstRoot, roots);
+ processRoots(diags, roots, tempRootsDir);
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");
+ if (dylibCacheDir.empty() && artifactDir.empty() && release.empty()) {
+ fprintf(stderr, "you must specify either -dylib_cache, -artifact or -release\n");
exit(-1);
} else if (!dylibCacheDir.empty() && !release.empty()) {
- fprintf(stderr, "you may not use -dylib_cache and -release at the same time");
+ fprintf(stderr, "you may not use -dylib_cache and -release at the same time\n");
+ exit(-1);
+ } else if (!dylibCacheDir.empty() && !artifactDir.empty()) {
+ fprintf(stderr, "you may not use -dylib_cache and -artifact at the same time\n");
exit(-1);
}
exit(-1);
}
+ if (!baselineDifferenceResultPath.empty() && (roots.size() > 1)) {
+ fprintf(stderr, "Cannot use -baseline_diff_results with more that one -root\n");
+ exit(-1);
+ }
+
+ if (!artifactDir.empty()) {
+ // Find the dylib cache dir from inside the artifact dir
+ struct stat stat_buf;
+ if (stat(artifactDir.c_str(), &stat_buf) != 0) {
+ fprintf(stderr, "Could not find artifact path '%s'\n", artifactDir.c_str());
+ exit(-1);
+ }
+ std::string dir = artifactDir + "/AppleInternal/Developer/DylibCaches";
+ if (stat(dir.c_str(), &stat_buf) != 0) {
+ fprintf(stderr, "Could not find artifact path '%s'\n", dir.c_str());
+ exit(-1);
+ }
+
+ if (!release.empty()) {
+ // Use the given release
+ dylibCacheDir = dir + "/" + release + ".dlc";
+ } else {
+ // Find a release directory
+ __block std::vector<std::string> subDirectories;
+ iterateDirectoryTree("", dir, ^(const std::string& dirPath) {
+ subDirectories.push_back(dirPath);
+ return false;
+ }, nullptr, false, false);
+
+ if (subDirectories.empty()) {
+ fprintf(stderr, "Could not find dlc subdirectories inside '%s'\n", dir.c_str());
+ exit(-1);
+ }
+
+ if (subDirectories.size() > 1) {
+ fprintf(stderr, "Found too many subdirectories inside artifact path '%s'. Use -release to select one\n", dir.c_str());
+ exit(-1);
+ }
+
+ dylibCacheDir = subDirectories.front();
+ }
+ }
+
if (dylibCacheDir.empty()) {
dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc";
}
chdir(dylibCacheDir.c_str());
dispatch_async(dispatch_get_main_queue(), ^{
- auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots);
+ // If we only want a list of configuations, then tell the manifest to only parse the data and not
+ // actually get all the macho's.
+ bool onlyParseManifest = listConfigs && configuration.empty();
+ auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots, onlyParseManifest);
if (manifest.build().empty()) {
fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
manifest.forEachConfiguration([](const std::string& configName) {
printf("%s\n", configName.c_str());
});
+ // If we weren't passed a configuration then exit
+ if (configuration.empty())
+ exit(0);
}
if (!manifest.filterForConfig(configuration)) {
configuration.c_str(), manifest.build().c_str());
exit(-1);
}
- manifest.calculateClosure();
- std::vector<dyld3::BuildQueueEntry> buildQueue;
+ (void)mkpath_np((dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
+ bool cacheBuildSuccess = false;
+ if (useMRM) {
+
+ FILE* jsonFile = nullptr;
+ if (!emitJSONPath.empty()) {
+ jsonFile = fopen(emitJSONPath.c_str(), "w");
+ if (!jsonFile) {
+ diags.verbose("can't open file '%s', errno=%d\n", emitJSONPath.c_str(), errno);
+ return;
+ }
+ }
+ dyld3::json::Node buildInvocationNode;
+
+ // Find the archs for the configuration we want.
+ __block std::set<std::string> validArchs;
+ manifest.configuration(configuration).forEachArchitecture(^(const std::string& path) {
+ validArchs.insert(path);
+ });
+
+ if (validArchs.size() != 1) {
+ fprintf(stderr, "MRM doesn't support more than one arch per configuration: %s\n",
+ configuration.c_str());
+ exit(-1);
+ }
+
+ const char* archs[validArchs.size()];
+ uint64_t archIndex = 0;
+ for (const std::string& arch : validArchs) {
+ archs[archIndex++] = arch.c_str();
+ }
+
+ BuildOptions_v1 buildOptions;
+ buildOptions.version = 1;
+ buildOptions.updateName = manifest.build().c_str();
+ buildOptions.deviceName = configuration.c_str();
+ buildOptions.disposition = Disposition::Unknown;
+ buildOptions.platform = (Platform)manifest.platform();
+ buildOptions.archs = archs;
+ buildOptions.numArchs = validArchs.size();
+ buildOptions.verboseDiagnostics = debug;
+ buildOptions.isLocallyBuiltCache = true;
+
+ __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
+ buildInvocationNode.map["build-options"] = getBuildOptionsNode(buildOptions);
+
+ std::set<std::string> requiredBinaries = {
+ "/usr/lib/libSystem.B.dylib"
+ };
+
+ // Get the file data for every MachO in the BOM.
+ __block dyld3::json::Node filesNode;
+ __block std::vector<std::pair<const void*, size_t>> mappedFiles;
+ manifest.forEachMachO(configuration, ^(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf) {
+
+ // Filter based on arch as the Manifest adds the file once for each UUID.
+ if (!validArchs.count(arch))
+ return;
+
+ struct stat stat_buf;
+ int fd = ::open(buildPath.c_str(), O_RDONLY, 0);
+ if (fd == -1) {
+ diags.verbose("can't open file '%s', errno=%d\n", buildPath.c_str(), errno);
+ return;
+ }
+
+ if (fstat(fd, &stat_buf) == -1) {
+ diags.verbose("can't stat open file '%s', errno=%d\n", buildPath.c_str(), errno);
+ ::close(fd);
+ return;
+ }
+
+ const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buffer == MAP_FAILED) {
+ diags.verbose("mmap() for file at %s failed, errno=%d\n", buildPath.c_str(), errno);
+ ::close(fd);
+ }
+ ::close(fd);
+
+ mappedFiles.emplace_back(buffer, (size_t)stat_buf.st_size);
+ FileFlags fileFlags = FileFlags::NoFlags;
+ if (requiredBinaries.count(runtimePath))
+ fileFlags = FileFlags::RequiredClosure;
+ addFile(sharedCacheBuilder, runtimePath.c_str(), (uint8_t*)buffer, (size_t)stat_buf.st_size, fileFlags);
+
+ dyld3::json::Node fileNode;
+ fileNode.map["path"].value = runtimePath;
+ fileNode.map["flags"].value = "NoFlags";
+ filesNode.array.push_back(fileNode);
+ });
+
+ __block dyld3::json::Node symlinksNode;
+ manifest.forEachSymlink(configuration, ^(const std::string &fromPath, const std::string &toPath) {
+ addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
+
+ dyld3::json::Node symlinkNode;
+ symlinkNode.map["from-path"].value = fromPath;
+ symlinkNode.map["to-path"].value = toPath;
+ symlinksNode.array.push_back(symlinkNode);
+ });
+
+ buildInvocationNode.map["symlinks"] = symlinksNode;
+
+ std::string orderFileData;
+ if (!manifest.dylibOrderFile().empty()) {
+ orderFileData = loadOrderFile(manifest.dylibOrderFile());
+ if (!orderFileData.empty()) {
+ addFile(sharedCacheBuilder, "*order file data*", (uint8_t*)orderFileData.data(), orderFileData.size(), FileFlags::DylibOrderFile);
+ dyld3::json::Node fileNode;
+ fileNode.map["path"].value = manifest.dylibOrderFile();
+ fileNode.map["flags"].value = "DylibOrderFile";
+ filesNode.array.push_back(fileNode);
+ }
+ }
+
+ std::string dirtyDataOrderFileData;
+ if (!manifest.dirtyDataOrderFile().empty()) {
+ dirtyDataOrderFileData = loadOrderFile(manifest.dirtyDataOrderFile());
+ if (!dirtyDataOrderFileData.empty()) {
+ addFile(sharedCacheBuilder, "*dirty data order file data*", (uint8_t*)dirtyDataOrderFileData.data(), dirtyDataOrderFileData.size(), FileFlags::DirtyDataOrderFile);
+ dyld3::json::Node fileNode;
+ fileNode.map["path"].value = manifest.dirtyDataOrderFile();
+ fileNode.map["flags"].value = "DirtyDataOrderFile";
+ filesNode.array.push_back(fileNode);
+ }
+ }
+
+ buildInvocationNode.map["files"] = filesNode;
+
+ if (jsonFile) {
+ dyld3::json::printJSON(buildInvocationNode, 0, jsonFile);
+ fclose(jsonFile);
+ jsonFile = nullptr;
+ }
+
+ cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
+
+ if (!cacheBuildSuccess) {
+ for (uint64 i = 0, e = getErrorCount(sharedCacheBuilder); i != e; ++i) {
+ const char* errorMessage = getError(sharedCacheBuilder, i);
+ fprintf(stderr, "ERROR: %s\n", errorMessage);
+ }
+ }
+
+ // Now emit each cache we generated, or the errors for them.
+ for (uint64 i = 0, e = getCacheResultCount(sharedCacheBuilder); i != e; ++i) {
+ BuildResult result;
+ getCacheResult(sharedCacheBuilder, i, &result);
+ if (result.numErrors) {
+ for (uint64_t errorIndex = 0; errorIndex != result.numErrors; ++errorIndex) {
+ fprintf(stderr, "[%s] ERROR: %s\n", result.loggingPrefix, result.errors[errorIndex]);
+ }
+ cacheBuildSuccess = false;
+ continue;
+ }
+ if (result.numWarnings) {
+ for (uint64_t warningIndex = 0; warningIndex != result.numWarnings; ++warningIndex) {
+ fprintf(stderr, "[%s] WARNING: %s\n", result.loggingPrefix, result.warnings[warningIndex]);
+ }
+ }
+ }
+
+ // If we built caches, then write everything out.
+ // TODO: Decide if we should we write any good caches anyway?
+ if (cacheBuildSuccess) {
+ for (uint64 i = 0, e = getFileResultCount(sharedCacheBuilder); i != e; ++i) {
+ FileResult result;
+ getFileResult(sharedCacheBuilder, i, &result);
+
+ if (!result.data)
+ continue;
+
+ const std::string path = dstRoot + result.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 ) {
+ ::ftruncate(fd, result.size);
+ uint64_t writtenSize = pwrite(fd, result.data, result.size, 0);
+ if ( writtenSize == result.size ) {
+ ::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);
+ continue; // success
+ }
+ }
+ else {
+ fprintf(stderr, "ERROR: could not write file %s\n", pathTemplateSpace);
+ cacheBuildSuccess = false;
+ }
+ ::close(fd);
+ ::unlink(pathTemplateSpace);
+ }
+ else {
+ fprintf(stderr, "ERROR: could not open file %s\n", pathTemplateSpace);
+ cacheBuildSuccess = false;
+ }
+ }
+ }
+
+ destroySharedCacheBuilder(sharedCacheBuilder);
- bool cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false);
+ for (auto mappedFile : mappedFiles)
+ ::munmap((void*)mappedFile.first, mappedFile.second);
+ } else {
+ manifest.calculateClosure();
+
+ cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false, emitDevCaches, true);
+ }
if (!cacheBuildSuccess) {
exit(-1);
}
- writeRootList(dstRoot, roots);
+ // Compare this cache to the baseline cache and see if we have any roots to copy over
+ if (!baselineDifferenceResultPath.empty() || baselineCopyRoots) {
+ std::set<std::string> baselineDylibs = manifest.resultsForConfiguration(configuration);
+
+ std::set<std::string> newDylibs;
+ manifest.forEachConfiguration([&manifest, &newDylibs](const std::string& configName) {
+ for (auto& arch : manifest.configuration(configName).architectures) {
+ for (auto& dylib : arch.second.results.dylibs) {
+ if (dylib.second.included) {
+ newDylibs.insert(manifest.installNameForUUID(dylib.first));
+ }
+ }
+ }
+ });
+
+ if (baselineCopyRoots) {
+ // Work out the set of dylibs in the old cache but not the new one
+ std::set<std::string> dylibsMissingFromNewCache;
+ for (const std::string& baselineDylib : baselineDylibs) {
+ if (!newDylibs.count(baselineDylib))
+ dylibsMissingFromNewCache.insert(baselineDylib);
+ }
+
+ if (!dylibsMissingFromNewCache.empty()) {
+ BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
+ BOMCopierSetUserData(copier, (void*)&dylibsMissingFromNewCache);
+ BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
+ std::string dylibCacheRootDir = realFilePath(dylibCacheDir + "/Root");
+ if (dylibCacheRootDir == "") {
+ fprintf(stderr, "Could not find dylib Root directory to copy baseline roots from\n");
+ exit(1);
+ }
+ BOMCopierCopy(copier, dylibCacheRootDir.c_str(), dstRoot.c_str());
+ BOMCopierFree(copier);
+
+ for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCache) {
+ diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.c_str());
+ }
+ }
+ }
+
+ if (!baselineDifferenceResultPath.empty()) {
+ auto cppToObjStr = [](const std::string& str) {
+ return [NSString stringWithUTF8String:str.c_str()];
+ };
+
+ // Work out the set of dylibs in the cache and taken from the -root
+ NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
+ for (auto& root : roots) {
+ for (const std::string& dylibInstallName : newDylibs) {
+ struct stat sb;
+ std::string filePath = root + "/" + dylibInstallName;
+ if (!stat(filePath.c_str(), &sb)) {
+ [dylibsFromRoots addObject:cppToObjStr(dylibInstallName)];
+ }
+ }
+ }
+
+ // Work out the set of dylibs in the new cache but not in the baseline cache.
+ NSMutableArray<NSString*>* dylibsMissingFromBaselineCache = [NSMutableArray array];
+ for (const std::string& newDylib : newDylibs) {
+ if (!baselineDylibs.count(newDylib))
+ [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
+ }
+
+ NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
+ cacheDict[@"root-paths-in-cache"] = dylibsFromRoots;
+ cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache;
+
+ NSError* error = nil;
+ NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
+ format:NSPropertyListBinaryFormat_v1_0
+ options:0
+ error:&error];
+ (void)[outData writeToFile:cppToObjStr(baselineDifferenceResultPath) atomically:YES];
+ }
+ }
if (copyRoots) {
- manifest.forEachConfiguration([&manifest](const std::string& configName) {
+ std::set<std::string> cachePaths;
+ manifest.forEachConfiguration([&manifest, &cachePaths](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());
+ cachePaths.insert(manifest.installNameForUUID(dylib.first));
}
}
}
});
BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
- BOMCopierSetCopyFileStartedHandler(copier, filteredCopy);
+ BOMCopierSetUserData(copier, (void*)&cachePaths);
+ BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths);
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.
const char* args[8];
args[0] = (char*)"/bin/rm";
args[1] = (char*)"-rf";
- args[2] = (char*)tempRootDir;
+ args[2] = (char*)tempRootsDir;
args[3] = nullptr;
(void)runCommandAndWait(diags, args);
#include <iostream>
#include <fstream>
-#include "MachOParser.h"
+#include "MachOFile.h"
#include "FileUtils.h"
#include "StringUtils.h"
#include "DyldSharedCache.h"
Diagnostics diag;
bool usedWholeFile = false;
for (MappedMachOsByCategory& file : files) {
- size_t sliceOffset;
- size_t sliceLength;
+ uint64_t sliceOffset = 0;
+ uint64_t sliceLength = statBuf.st_size;
bool fatButMissingSlice;
const void* slice = MAP_FAILED;
- if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+ const dyld3::FatFile* fh = (dyld3::FatFile*)wholeFile;
+ const dyld3::MachOFile* mh = (dyld3::MachOFile*)wholeFile;
+ if ( fh->isFatFileWithSlice(diag, statBuf.st_size, file.archName.c_str(), 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) ) {
+ mh = (dyld3::MachOFile*)slice;
+ if ( !mh->isMachO(diag, sliceLength) ) {
::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) ) {
+ else if ( !fatButMissingSlice && mh->isMachO(diag, sliceLength) ) {
slice = wholeFile;
sliceLength = statBuf.st_size;
sliceOffset = 0;
//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 ) {
+ mh = (dyld3::MachOFile*)slice;
+ if ( mh->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() ) {
+ if ( mh->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);
}
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;
}
break;
case dyld3::Platform::watchOS:
archStrs.insert("armv7k");
+ archStrs.insert("arm64_32");
break;
case dyld3::Platform::unknown:
case dyld3::Platform::macOS:
std::vector<MappedMachOsByCategory> allFileSets;
if ( archStrs.count("arm64") )
allFileSets.push_back({"arm64"});
+ if ( archStrs.count("arm64_32") )
+ allFileSets.push_back({"arm64_32"});
if ( archStrs.count("armv7k") )
allFileSets.push_back({"armv7k"});
std::vector<std::string> paths;
options.inodesAreSameAsRuntime = false;
options.cacheSupportsASLR = true;
options.forSimulator = false;
+ options.isLocallyBuiltCache = true;
options.verbose = verbose;
options.evictLeafDylibsOnOverflow = false;
options.pathPrefixes = { rootPath };
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * 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 "mrm_shared_cache_builder.h"
+#include "CacheBuilder.h"
+#include "ClosureFileSystem.h"
+#include "FileUtils.h"
+#include <pthread.h>
+#include <memory>
+#include <vector>
+#include <map>
+
+static const uint64_t kMinBuildVersion = 1; //The minimum version BuildOptions struct we can support
+static const uint64_t kMaxBuildVersion = 1; //The maximum version BuildOptions struct we can support
+
+namespace dyld3 {
+namespace closure {
+
+struct FileInfo {
+ const char* path;
+ const uint8_t* data;
+ const uint64_t length;
+ FileFlags flags;
+ uint64_t mtime;
+ uint64_t inode;
+};
+
+class FileSystemMRM : public FileSystem {
+public:
+ FileSystemMRM() : FileSystem() { }
+
+ bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override {
+ Diagnostics diag;
+ std::string resolvedPath = symlinkResolver.realPath(diag, possiblePath);
+ if (diag.hasError()) {
+ diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+ diag.clearError();
+ return false;
+ }
+
+ // FIXME: Should we only return real paths of files which point to macho's? For now that is what we are doing
+ auto it = fileMap.find(resolvedPath);
+ if (it == fileMap.end())
+ return false;
+
+ memcpy(realPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
+ return true;
+ }
+
+ bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override {
+ Diagnostics diag;
+ std::string resolvedPath = symlinkResolver.realPath(diag, path);
+ if (diag.hasError()) {
+ diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+ diag.clearError();
+ return false;
+ }
+
+ auto it = fileMap.find(resolvedPath);
+ if (it == fileMap.end())
+ return false;
+
+ if (resolvedPath == path)
+ realerPath[0] = '\0';
+ else
+ memcpy(realerPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
+
+ // The file exists at this exact path. Lets use it!
+ const FileInfo& fileInfo = files[it->second];
+
+ info.fileContent = fileInfo.data;
+ info.fileContentLen = fileInfo.length;
+ info.sliceOffset = 0;
+ info.sliceLen = fileInfo.length;
+ info.inode = fileInfo.inode;
+ info.mtime = fileInfo.mtime;
+ info.unload = nullptr;
+ info.path = path;
+ return true;
+ }
+
+ void unloadFile(const LoadedFileInfo& info) const override {
+ if (info.unload)
+ info.unload(info);
+ }
+
+ void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override {
+ // Note we don't actually unload the data here, but we do want to update the offsets for other data structures to track where we are
+ info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
+ info.fileContentLen = keepLength;
+ }
+
+ bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const override {
+ Diagnostics diag;
+ std::string resolvedPath = symlinkResolver.realPath(diag, path);
+ if (diag.hasError()) {
+ diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+ diag.clearError();
+ return false;
+ }
+
+ auto it = fileMap.find(resolvedPath);
+ if (it == fileMap.end())
+ return false;
+
+ // The file exists at this exact path. Lets use it!
+ const FileInfo& fileInfo = files[it->second];
+ if (inode)
+ *inode = fileInfo.inode;
+ if (mtime)
+ *mtime = fileInfo.mtime;
+ if (issetuid)
+ *issetuid = false;
+ return true;
+ }
+
+ // MRM file APIs
+ bool addFile(const char* path, uint8_t* data, uint64_t size, Diagnostics& diag, FileFlags fileFlags) {
+ auto iteratorAndInserted = fileMap.insert(std::make_pair(path, files.size()));
+ if (!iteratorAndInserted.second) {
+ diag.error("Already have content for path: '%s'", path);
+ return false;
+ }
+
+ symlinkResolver.addFile(diag, path);
+ if (diag.hasError())
+ return false;
+
+ // on iOS, inode is used to hold hash of path
+ uint64_t hash = 0;
+ for (const char* s = path; *s != '\0'; ++s)
+ hash += hash*4 + *s;
+ uint64_t inode = hash;
+ uint64_t mtime = 0;
+
+ files.push_back((FileInfo){ path, data, size, fileFlags, mtime, inode });
+ return true;
+ }
+
+ bool addSymlink(const char* fromPath, const char* toPath, Diagnostics& diag) {
+ symlinkResolver.addSymlink(diag, fromPath, toPath);
+ return !diag.hasError();
+ }
+
+ void forEachFileInfo(std::function<void(const char* path, FileFlags fileFlags)> lambda) {
+ for (const FileInfo& fileInfo : files)
+ lambda(fileInfo.path, fileInfo.flags);
+ }
+
+ size_t fileCount() const {
+ return files.size();
+ }
+
+ std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diag) {
+ return symlinkResolver.getResolvedSymlinks(diag);
+ }
+
+private:
+ std::vector<FileInfo> files;
+ std::map<std::string, uint64_t> fileMap;
+ SymlinkResolver symlinkResolver;
+};
+
+} // namespace closure
+} // namespace dyld3
+
+struct BuildInstance {
+ std::unique_ptr<DyldSharedCache::CreateOptions> options;
+ std::unique_ptr<CacheBuilder> builder;
+ std::vector<CacheBuilder::InputFile> inputFiles;
+ std::vector<const char*> errors;
+ std::vector<const char*> warnings;
+ std::vector<std::string> errorStrings; // Owns the data for the errors
+ std::vector<std::string> warningStrings; // Owns the data for the warnings
+ uint8_t* cacheData = nullptr;
+ uint64_t cacheSize = 0;
+ uint8_t* cacheMapData = nullptr;
+ uint64_t cacheMapSize = 0;
+ std::string cdHash; // Owns the data for the cdHash
+};
+
+struct BuildFileResult {
+ std::string path;
+ const uint8_t* data;
+ uint64_t size;
+};
+
+struct SharedCacheBuilder {
+ SharedCacheBuilder(const BuildOptions_v1* options);
+ const BuildOptions_v1* options;
+ dyld3::closure::FileSystemMRM fileSystem;
+
+ std::string dylibOrderFileData;
+ std::string dirtyDataOrderFileData;
+
+ // An array of builders and their options as we may have more than one builder for a given device variant.
+ std::vector<BuildInstance> builders;
+
+ // The results from all of the builders
+ // We keep this in a vector to own the data.
+ std::vector<BuildFileResult> fileResults;
+
+ std::vector<std::string> errors;
+ pthread_mutex_t lock;
+
+ enum State {
+ AcceptingFiles,
+ Building,
+ FinishedBuilding
+ };
+
+ State state = AcceptingFiles;
+
+ void runSync(void (^block)()) {
+ pthread_mutex_lock(&lock);
+ block();
+ pthread_mutex_unlock(&lock);
+ }
+
+ __attribute__((format(printf, 2, 3)))
+ void error(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ Diagnostics diag;
+ diag.error(format, list);
+ va_end(list);
+
+ errors.push_back(diag.errorMessage());
+ }
+};
+
+SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
+
+}
+
+void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& builder) {
+ if (options->version < kMinBuildVersion) {
+ builder.error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
+ }
+ if (options->version > kMaxBuildVersion) {
+ builder.error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion);
+ }
+ if (!options->updateName) {
+ builder.error("updateName must not be null");
+ }
+ if (!options->deviceName) {
+ builder.error("deviceName must not be null");
+ }
+ switch (options->disposition) {
+ case Disposition::Unknown:
+ case Disposition::InternalDevelopment:
+ case Disposition::Customer:
+ break;
+ default:
+ builder.error("unknown disposition value");
+ break;
+ }
+ switch (options->platform) {
+ case Platform::unknown:
+ builder.error("platform must not be unknown");
+ break;
+ case Platform::macOS:
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::watchOS:
+ case Platform::bridgeOS:
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ case Platform::watchOS_simulator:
+ break;
+ default:
+ builder.error("unknown platform value");
+ break;
+ }
+ if (!options->archs) {
+ builder.error("archs must not be null");
+ }
+ if (!options->numArchs) {
+ builder.error("numArchs must not be 0");
+ }
+}
+
+struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) {
+ SharedCacheBuilder* builder = new SharedCacheBuilder(options);
+
+ // Check the option struct values are valid
+ validiateBuildOptions(options, *builder);
+
+ return builder;
+}
+
+bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
+ __block bool success = false;
+ builder->runSync(^() {
+ if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+ builder->error("Cannot add file: '%s' as we have already started building", path);
+ return;
+ }
+ size_t pathLength = strlen(path);
+ if (pathLength == 0) {
+ builder->error("Empty path");
+ return;
+ }
+ if (pathLength >= MAXPATHLEN) {
+ builder->error("Path is too long: '%s'", path);
+ return;
+ }
+ if (data == nullptr) {
+ builder->error("Data cannot be null for file: '%s'", path);
+ return;
+ }
+ switch (fileFlags) {
+ case NoFlags:
+ case MustBeInCache:
+ case ShouldBeExcludedFromCacheIfUnusedLeaf:
+ case RequiredClosure:
+ break;
+ case DylibOrderFile:
+ builder->dylibOrderFileData = std::string((char*)data, size);
+ success = true;
+ return;
+ case DirtyDataOrderFile:
+ builder->dirtyDataOrderFileData = std::string((char*)data, size);
+ success = true;
+ return;
+ default:
+ builder->error("unknown file flags value");
+ break;
+ }
+ Diagnostics diag;
+ if (!builder->fileSystem.addFile(path, data, size, diag, fileFlags)) {
+ builder->errors.push_back(diag.errorMessage());
+ return;
+ }
+ success = true;
+ });
+ return success;
+}
+
+bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
+ __block bool success = false;
+ builder->runSync(^() {
+ if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+ builder->error("Cannot add file: '%s' as we have already started building", fromPath);
+ return;
+ }
+ size_t pathLength = strlen(fromPath);
+ if (pathLength == 0) {
+ builder->error("Empty path");
+ return;
+ }
+ if (pathLength >= MAXPATHLEN) {
+ builder->error("Path is too long: '%s'", fromPath);
+ return;
+ }
+ Diagnostics diag;
+ if (!builder->fileSystem.addSymlink(fromPath, toPath, diag)) {
+ builder->errors.push_back(diag.errorMessage());
+ return;
+ }
+ success = true;
+ });
+ return success;
+}
+
+static bool platformExcludeLocalSymbols(Platform platform) {
+ switch (platform) {
+ case Platform::unknown:
+ case Platform::macOS:
+ return false;
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::watchOS:
+ case Platform::bridgeOS:
+ return true;
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ case Platform::watchOS_simulator:
+ return false;
+ }
+}
+
+static DyldSharedCache::CodeSigningDigestMode platformCodeSigningDigestMode(Platform platform) {
+ switch (platform) {
+ case Platform::unknown:
+ case Platform::macOS:
+ case Platform::iOS:
+ case Platform::tvOS:
+ return DyldSharedCache::SHA256only;
+ case Platform::watchOS:
+ return DyldSharedCache::Agile;
+ case Platform::bridgeOS:
+ case Platform::iOSMac:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ case Platform::watchOS_simulator:
+ return DyldSharedCache::SHA256only;
+ }
+}
+
+static bool platformIsForSimulator(Platform platform) {
+ switch (platform) {
+ case Platform::unknown:
+ case Platform::macOS:
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::watchOS:
+ case Platform::bridgeOS:
+ case Platform::iOSMac:
+ return false;
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ case Platform::watchOS_simulator:
+ return true;
+ }
+}
+
+static const char* dispositionName(Disposition disposition) {
+ switch (disposition) {
+ case Disposition::Unknown:
+ return "";
+ case Disposition::InternalDevelopment:
+ return "Internal";
+ case Disposition::Customer:
+ return "Customer";
+ case Disposition::InternalMinDevelopment:
+ return "InternalMinDevelopment";
+ }
+}
+
+bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
+ __block bool success = false;
+ builder->runSync(^() {
+ if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+ builder->error("Builder has already been run");
+ return;
+ }
+ builder->state = SharedCacheBuilder::Building;
+ if (builder->fileSystem.fileCount() == 0) {
+ builder->error("Cannot run builder with no files");
+ }
+
+ Diagnostics diag;
+ std::vector<DyldSharedCache::FileAlias> aliases = builder->fileSystem.getResolvedSymlinks(diag);
+ if (diag.hasError()) {
+ diag.verbose("Symlink resolver error: %s\n", diag.errorMessage().c_str());
+ }
+
+ if (!builder->errors.empty()) {
+ builder->error("Skipping running shared cache builder due to previous errors");
+ return;
+ }
+
+ __block std::vector<CacheBuilder::InputFile> inputFiles;
+ builder->fileSystem.forEachFileInfo(^(const char* path, FileFlags fileFlags) {
+ CacheBuilder::InputFile::State state = CacheBuilder::InputFile::Unset;
+ switch (fileFlags) {
+ case FileFlags::NoFlags:
+ state = CacheBuilder::InputFile::Unset;
+ break;
+ case FileFlags::MustBeInCache:
+ state = CacheBuilder::InputFile::MustBeIncluded;
+ break;
+ case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
+ state = CacheBuilder::InputFile::MustBeExcludedIfUnused;
+ break;
+ case FileFlags::RequiredClosure:
+ state = CacheBuilder::InputFile::MustBeIncluded;
+ break;
+ case FileFlags::DylibOrderFile:
+ case FileFlags::DirtyDataOrderFile:
+ builder->error("Order files should not be in the file system");
+ return;
+ }
+ inputFiles.emplace_back((CacheBuilder::InputFile){ path, state });
+ });
+
+ auto addCacheConfiguration = ^(bool isOptimized) {
+ for (uint64_t i = 0; i != builder->options->numArchs; ++i) {
+ auto options = std::make_unique<DyldSharedCache::CreateOptions>((DyldSharedCache::CreateOptions){});
+ const char *cacheSuffix = (isOptimized ? "" : ".development");
+ std::string runtimePath = (builder->options->platform == Platform::macOS) ? "/private/var/db/dyld/" : "/System/Library/Caches/com.apple.dyld/";
+ options->outputFilePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i] + cacheSuffix;
+ options->outputMapFilePath = options->outputFilePath + ".map";
+ options->archName = builder->options->archs[i];
+ options->platform = (dyld3::Platform)builder->options->platform;
+ options->excludeLocalSymbols = platformExcludeLocalSymbols(builder->options->platform);
+ options->optimizeStubs = isOptimized;
+ options->optimizeObjC = true;
+ options->codeSigningDigestMode = platformCodeSigningDigestMode(builder->options->platform);
+ options->dylibsRemovedDuringMastering = true;
+ options->inodesAreSameAsRuntime = false;
+ options->cacheSupportsASLR = true;
+ options->forSimulator = platformIsForSimulator(builder->options->platform);
+ options->isLocallyBuiltCache = builder->options->isLocallyBuiltCache;
+ options->verbose = builder->options->verboseDiagnostics;
+ options->evictLeafDylibsOnOverflow = true;
+ options->loggingPrefix = std::string(builder->options->deviceName) + dispositionName(builder->options->disposition) + "." + builder->options->archs[i] + cacheSuffix;
+ options->pathPrefixes = { "" };
+ options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
+ options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
+
+ auto cacheBuilder = std::make_unique<CacheBuilder>(*options.get(), builder->fileSystem);
+ builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
+ }
+ };
+
+ // Enqueue a cache for each configuration
+ switch (builder->options->disposition) {
+ case Disposition::Unknown:
+ case Disposition::InternalDevelopment:
+ addCacheConfiguration(false);
+ addCacheConfiguration(true);
+ break;
+ case Disposition::Customer:
+ addCacheConfiguration(true);
+ case Disposition::InternalMinDevelopment:
+ addCacheConfiguration(false);
+ }
+
+ // FIXME: This step can run in parallel.
+ for (auto& buildInstance : builder->builders) {
+ CacheBuilder* builder = buildInstance.builder.get();
+ builder->build(buildInstance.inputFiles, aliases);
+
+ // First put the warnings in to a vector to own them.
+ buildInstance.warningStrings.reserve(builder->warnings().size());
+ for (const std::string& warning : builder->warnings())
+ buildInstance.warningStrings.push_back(warning);
+
+ // Then copy to a vector to reference the owner
+ buildInstance.warnings.reserve(buildInstance.warningStrings.size());
+ for (const std::string& warning : buildInstance.warningStrings)
+ buildInstance.warnings.push_back(warning.c_str());
+
+ if (!builder->errorMessage().empty()) {
+ // First put the errors in to a vector to own them.
+ buildInstance.errorStrings.push_back(builder->errorMessage());
+
+ // Then copy to a vector to reference the owner
+ buildInstance.errors.reserve(buildInstance.errorStrings.size());
+ for (const std::string& error : buildInstance.errorStrings)
+ buildInstance.errors.push_back(error.c_str());
+ }
+
+ if (builder->errorMessage().empty()) {
+ builder->writeBuffer(buildInstance.cacheData, buildInstance.cacheSize);
+ builder->writeMapFileBuffer(buildInstance.cacheMapData, buildInstance.cacheMapSize);
+ buildInstance.cdHash = builder->cdHashFirst();
+ }
+ }
+
+ // Now that we have run all of the builds, collect the results
+ // First push file results for each of the shared caches we built
+ __block std::map<std::string, uint32_t> dylibsInCaches;
+ for (auto& buildInstance : builder->builders) {
+ CacheBuilder* cacheBuilder = buildInstance.builder.get();
+ if (!cacheBuilder->errorMessage().empty())
+ continue;
+ builder->fileResults.push_back((BuildFileResult) { buildInstance.options->outputFilePath, buildInstance.cacheData, buildInstance.cacheSize });
+ builder->fileResults.push_back((BuildFileResult) { buildInstance.options->outputMapFilePath, buildInstance.cacheMapData, buildInstance.cacheMapSize });
+
+ cacheBuilder->forEachCacheDylib(^(const std::string &path) {
+ ++dylibsInCaches[path];
+ });
+ }
+
+ // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
+ const size_t numCaches = builder->builders.size();
+ for (const auto& dylibAndCount : dylibsInCaches) {
+ if (dylibAndCount.second == numCaches)
+ builder->fileResults.push_back((BuildFileResult) { dylibAndCount.first, nullptr, 0 });
+ }
+
+ builder->state = SharedCacheBuilder::FinishedBuilding;
+ success = true;
+ });
+ return success;
+}
+
+uint64_t getErrorCount(const struct SharedCacheBuilder* builder) {
+ return builder->errors.size();
+}
+
+const char* getError(const struct SharedCacheBuilder* builder, uint64_t errorIndex) {
+ if (errorIndex >= builder->errors.size())
+ return nullptr;
+ return builder->errors[errorIndex].c_str();
+}
+
+uint64_t getCacheResultCount(const struct SharedCacheBuilder* builder) {
+ return builder->builders.size();
+}
+
+void getCacheResult(struct SharedCacheBuilder* builder, uint64_t cacheIndex, BuildResult* result) {
+ if (cacheIndex >= builder->builders.size())
+ return;
+
+ BuildInstance& buildInstance = builder->builders[cacheIndex];
+
+ result->version = 1;
+ result->loggingPrefix = buildInstance.options->loggingPrefix.c_str();
+ result->warnings = buildInstance.warnings.empty() ? nullptr : buildInstance.warnings.data();
+ result->numWarnings = buildInstance.warnings.size();
+ result->errors = buildInstance.errors.empty() ? nullptr : buildInstance.errors.data();
+ result->numErrors = buildInstance.errors.size();
+ result->sharedCachePath = buildInstance.options->outputFilePath.c_str();
+ result->cdHash = buildInstance.cdHash.c_str();
+}
+
+uint64_t getFileResultCount(const struct SharedCacheBuilder* builder) {
+ return builder->fileResults.size();
+}
+
+void getFileResult(struct SharedCacheBuilder* builder, uint64_t fileIndex, FileResult* result) {
+ if (fileIndex >= builder->fileResults.size())
+ return;
+ const BuildFileResult& buildFileResult = builder->fileResults[fileIndex];
+ *result = (FileResult) { buildFileResult.path.c_str(), buildFileResult.data, buildFileResult.size };
+}
+
+void destroySharedCacheBuilder(struct SharedCacheBuilder* builder) {
+ delete builder;
+}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * 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 mrm_shared_cache_builder_hpp
+#define mrm_shared_cache_builder_hpp
+
+#include <Availability.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note, this should match PLATFORM_* values in <mach-o/loader.h>
+enum Platform {
+ unknown = 0,
+ macOS = 1, // PLATFORM_MACOS
+ iOS = 2, // PLATFORM_IOS
+ tvOS = 3, // PLATFORM_TVOS
+ watchOS = 4, // PLATFORM_WATCHOS
+ bridgeOS = 5, // PLATFORM_BRIDGEOS
+ iOSMac = 6, // PLATFORM_IOSMAC
+ iOS_simulator = 7, // PLATFORM_IOSIMULATOR
+ tvOS_simulator = 8, // PLATFORM_TVOSSIMULATOR
+ watchOS_simulator = 9 // PLATFORM_WATCHOSSIMULATOR
+};
+
+enum Disposition
+{
+ Unknown = 0,
+ InternalDevelopment = 1,
+ Customer = 2,
+ InternalMinDevelopment = 3
+};
+
+enum FileFlags
+{
+ // Note these are for macho inputs
+ NoFlags = 0,
+ MustBeInCache = 1,
+ ShouldBeExcludedFromCacheIfUnusedLeaf = 2,
+ RequiredClosure = 3,
+
+ // These are for the order files
+ DylibOrderFile = 100,
+ DirtyDataOrderFile = 101
+};
+
+struct BuildOptions_v1
+{
+ uint64_t version; // Future proofing, set to 1
+ const char * updateName; // BuildTrain+UpdateNumber
+ const char * deviceName;
+ enum Disposition disposition; // Internal, Customer, etc.
+ enum Platform platform; // Enum: unknown, macOS, iOS, ...
+ const char ** archs;
+ uint64_t numArchs;
+ bool verboseDiagnostics;
+ bool isLocallyBuiltCache;
+};
+
+struct BuildResult {
+ uint64_t version; // Future proofing, set to 1
+ const char* loggingPrefix;
+ const char ** warnings;
+ uint64_t numWarnings;
+ const char ** errors;
+ uint64_t numErrors;
+ const char* sharedCachePath;
+ const char* cdHash;
+};
+
+struct FileResult {
+ const char* path;
+ const uint8_t* data; // Owned by the cache builder. Destroyed by destroySharedCacheBuilder
+ uint64_t size;
+};
+
+struct SharedCacheBuilder;
+
+__API_AVAILABLE(macos(10.12))
+struct SharedCacheBuilder* createSharedCacheBuilder(const struct BuildOptions_v1* options);
+
+// Add a file. Returns true on success.
+__API_AVAILABLE(macos(10.12))
+bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, enum FileFlags fileFlags);
+
+__API_AVAILABLE(macos(10.12))
+bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath);
+
+__API_AVAILABLE(macos(10.12))
+bool runSharedCacheBuilder(struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getErrorCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+const char* getError(const struct SharedCacheBuilder* builder, uint64_t errorIndex);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getCacheResultCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+void getCacheResult(struct SharedCacheBuilder* builder, uint64_t cacheIndex, struct BuildResult* result);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getFileResultCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+void getFileResult(struct SharedCacheBuilder* builder, uint64_t fileIndex, struct FileResult* result);
+
+__API_AVAILABLE(macos(10.12))
+void destroySharedCacheBuilder(struct SharedCacheBuilder* builder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* mrm_shared_cache_builder_hpp */
makeBoms(manifest, masterDstRoot);
});
allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
- agileChooseSHA256CdHash);
+ agileChooseSHA256CdHash, true, false);
}
manifest.write(resultPath);
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
-#include <sys/xattr.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
+#include <mach-o/dyld.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
-#include "MachOParser.h"
#include "FileUtils.h"
#include "StringUtils.h"
#include "DyldSharedCache.h"
+#include "MachOFile.h"
+#include "MachOAnalyzer.h"
+#include "ClosureFileSystemPhysical.h"
struct MappedMachOsByCategory
{
"/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
+ "/System/Library/Developer/CoreSimulator/", // ignore Marzipan
};
-static bool addIfMachO(const std::string& pathPrefix, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, 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 )
+ if ( startsWith(runtimePath, "/usr/lib/system/introspection/") )
return false;
+
+ auto warningHandler = ^(const char* msg) {
+ if ( verbose )
+ fprintf(stderr, "update_dyld_shared_cache: warning: cannot build dlopen closure for '%s' because %s\n", runtimePath.c_str(), msg);
+ };
+
bool result = false;
- const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if ( wholeFile != MAP_FAILED ) {
+ for (MappedMachOsByCategory& file : files) {
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;
- }
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archName.c_str(), dyld3::Platform::macOS);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ if ( ma != nullptr ) {
+ bool sipProtected = false; // isProtectedBySIP(fd);
+ bool issetuid = false;
+ if ( ma->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, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
}
}
- 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);
+ else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
+ // when SIP is enabled, only dylib protected by SIP can go in cache
+ if ( !requireSIP || sipProtected )
+ file.dylibsForCache.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) )
+ file.otherDylibsAndBundles.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
}
- 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 ( ma->isDylib() ) {
+ std::string installName = ma->installName();
+ if ( startsWith(installName, "@") && !contains(runtimePath, ".app/") && !contains(runtimePath, ".xpc/") ) {
+ 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());
}
}
- 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);
+ else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) ) {
+ file.otherDylibsAndBundles.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
}
- result = true;
}
+ result = true;
}
- if ( !usedWholeFile )
- ::munmap((void*)wholeFile, statBuf.st_size);
}
- ::close(fd);
+
return result;
}
bool multiplePrefixes = (pathPrefixes.size() > 1);
for (const std::string& prefix : pathPrefixes) {
// get all files from overlay for this search dir
+ dyld3::closure::FileSystemPhysical fileSystem(prefix.c_str());
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)
return;
// if the file is mach-o, add to list
- if ( addIfMachO(prefix, path, statBuf, requireSIP, files) ) {
+ if ( addIfMachO(fileSystem, path, statBuf, requireSIP, files) ) {
if ( multiplePrefixes )
alreadyUsed.insert(path);
}
struct stat statBuf2;
std::string fullPath2 = prefix2 + runPath;
if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) {
- addIfMachO(prefix2, runPath, statBuf2, requireSIP, files);
+ dyld3::closure::FileSystemPhysical fileSystem(prefix2.c_str());
+ addIfMachO(fileSystem, runPath, statBuf2, requireSIP, files);
runtimePathsFound.insert(runPath);
break;
}
return true;
}
- dyld3::MachOParser parser(aFile.mh);
- const char* installName = parser.installName();
+ const char* installName = aFile.mh->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;
return false;
}
}
+ // <rdar://problem/38000411> also if runtime path is a symlink to install name
+ std::string fullRuntime = volumePrefix + aFile.runtimePath;
+ if ( realpath(fullRuntime.c_str(), resolvedPath) != NULL ) {
+ std::string resolvedSymlink = resolvedPath;
+ if ( !volumePrefix.empty() ) {
+ resolvedSymlink = resolvedSymlink.substr(volumePrefix.size());
+ }
+ if ( resolvedSymlink == installName ) {
+ 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;
}
std::unordered_map<std::string, std::string> installNameToFirstPath;
for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
- dyld3::MachOParser parser(aFile.mh);
- const char* installName = parser.installName();
+ const char* installName = aFile.mh->installName();
auto pos = installNameToFirstPath.find(installName);
if ( pos == installNameToFirstPath.end() ) {
installNameToFirstPath[installName] = aFile.runtimePath;
{
// 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());
+ [&](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
[&](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) {
+ aFile.mh->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool &stop) {
if ( strcmp(loadPath, "/usr/lib/libxcselect.dylib") == 0 )
isXcodeShim = true;
});
} \
} while ( 0 )
-int main(int argc, const char* argv[])
+int main(int argc, const char* argv[], const char* envp[])
{
std::string rootPath;
std::string overlayPath;
overlayPath = resolvedPath;
}
}
+
+ // <rdar://problem/36362221> update_dyld_shared_cache should re-exec() itself to a newer version
+ std::string newTool;
+ if ( !rootPath.empty() )
+ newTool = rootPath + "/usr/bin/update_dyld_shared_cache";
+ else if ( !overlayPath.empty() )
+ newTool = overlayPath + "/usr/bin/update_dyld_shared_cache";
+ if ( !newTool.empty() ) {
+ struct stat newToolStatBuf;
+ if ( stat(newTool.c_str(), &newToolStatBuf) == 0 ) {
+ char curToolPath[PATH_MAX];
+ uint32_t curToolPathsize = PATH_MAX;
+ int result = _NSGetExecutablePath(curToolPath, &curToolPathsize);
+ if ( result == 0 ) {
+ char resolvedCurToolPath[PATH_MAX];
+ if ( realpath(curToolPath, resolvedCurToolPath) != NULL ) {
+ // don't re-exec if we are already running that tool
+ if ( newTool != resolvedCurToolPath ) {
+ execve(newTool.c_str(), (char**)argv, (char**)envp);
+ }
+ }
+ }
+ }
+ }
+
//
// pathPrefixes for three modes:
// 1) no options: { "" } // search only boot volume
if ( cacheDir.empty() ) {
+ // if -cache_dir is not specified, then write() will eventually fail if we are not running as root
+ if ( geteuid() != 0 ) {
+ fprintf(stderr, "update_dyld_shared_cache: must be run as root (sudo)\n");
+ return 1;
+ }
+
// write cache file into -root or -overlay directory, if used
if ( rootPath != "/" )
cacheDir = rootPath + MACOSX_DYLD_SHARED_CACHE_DIR;
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);
+ fprintf(stderr, "update_dyld_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir.c_str(), err);
return 1;
}
std::string fullPath = prefix + runtimePath;
struct stat statBuf;
if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+ dyld3::closure::FileSystemPhysical fileSystem(prefix.c_str());
+ char resolvedPath[PATH_MAX];
+ if ( realpath(fullPath.c_str(), resolvedPath) != NULL ) {
+ std::string resolvedSymlink = resolvedPath;
+ if ( !rootPath.empty() ) {
+ resolvedSymlink = resolvedSymlink.substr(rootPath.size());
+ }
+ if ( (runtimePath != resolvedSymlink) && !contains(runtimePath, "InputContext") ) { //HACK remove InputContext when fixed
+ // path requested is a symlink path, check if real path already loaded
+ for (const DyldSharedCache::MappedMachO& aDylibMapping : fileSet.dylibsForCache) {
+ if ( aDylibMapping.runtimePath == resolvedSymlink ) {
+ if ( verbose )
+ fprintf(stderr, "verifySelfContained, redirect %s to %s\n", runtimePath.c_str(), aDylibMapping.runtimePath.c_str());
+ return aDylibMapping;
+ }
+ }
+ }
+ }
+
std::vector<MappedMachOsByCategory> mappedFiles;
mappedFiles.push_back({fileSet.archName});
- if ( addIfMachO(prefix, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
- if ( !mappedFiles.back().dylibsForCache.empty() )
+ if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
+ if ( !mappedFiles.back().dylibsForCache.empty() ) {
+ if ( verbose )
+ fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str());
return mappedFiles.back().dylibsForCache.back();
+ }
}
}
}
}
// 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, "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.otherDylibsAndBundles) {
// fprintf(stderr, " %s\n", aFile.runtimePath.c_str());
//}
- // Clear the UUID xattr for the existing cache.
- // This prevents the existing cache from being used by dyld3 as roots are probably involved
- if (removexattr(outFile.c_str(), "cacheUUID", 0) != 0) {
- fprintf(stderr, "update_dyld_shared_cache: warning: failure to remove UUID xattr on shared cache file %s with error %s\n", outFile.c_str(), strerror(errno));
- }
// build cache new cache file
DyldSharedCache::CreateOptions options;
+ options.outputFilePath = outFile;
+ options.outputMapFilePath = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
options.archName = fileSet.archName;
options.platform = dyld3::Platform::macOS;
options.excludeLocalSymbols = false;
options.inodesAreSameAsRuntime = true;
options.cacheSupportsASLR = (fileSet.archName != "i386");
options.forSimulator = false;
+ options.isLocallyBuiltCache = true;
options.verbose = verbose;
options.evictLeafDylibsOnOverflow = true;
options.pathPrefixes = pathPrefixes;
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;
+ if ( results.errorMessage.empty() ) {
+ wroteSomeCacheFile = true;
}
else {
- // save new cache file to disk and write new .map file
- assert(results.cacheContent != nullptr);
- if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) {
- fprintf(stderr, "update_dyld_shared_cache: could not write dyld cache file %s\n", outFile.c_str());
- cacheBuildFailure = true;
- }
- if ( !cacheBuildFailure ) {
- uuid_t cacheUUID;
- results.cacheContent->getUUID(cacheUUID);
- if (setxattr(outFile.c_str(), "cacheUUID", (const void*)&cacheUUID, sizeof(cacheUUID), 0, XATTR_CREATE) != 0) {
- fprintf(stderr, "update_dyld_shared_cache: warning: failure to set UUID xattr on shared cache file %s with error %s\n", outFile.c_str(), strerror(errno));
- }
- 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);
+ fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str());
+ cacheBuildFailure = true;
}
});
#include <iostream>
#include <fstream>
-#include "MachOParser.h"
+#include "MachOFile.h"
#include "FileUtils.h"
#include "StringUtils.h"
#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "ClosureFileSystemPhysical.h"
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)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, 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 ) {
+ for (MappedMachOsByCategory& file : files) {
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;
- }
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archName.c_str(), dyld3::Platform::macOS);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ if ( ma != nullptr ) {
+ bool sipProtected = false; // isProtectedBySIP(fd);
+ bool issetuid = false;
+ if ( ma->isDynamicExecutable() ) {
+ //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, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ result = true;
}
+ else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
+ file.dylibsForCache.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+ result = true;
+ }
}
- if ( !usedWholeFile )
- ::munmap((void*)wholeFile, statBuf.st_size);
- }
- ::close(fd);
+ }
return result;
}
skipDirs.insert(s);
for (const char* searchDir : sSearchDirs ) {
+ dyld3::closure::FileSystemPhysical fileSystem(simRuntimeRootPath.c_str());
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);
return;
// if the file is mach-o add to list
- addIfMachO(simRuntimeRootPath, path, statBuf, platform, files);
+ addIfMachO(fileSystem, path, statBuf, platform, files);
});
}
}
static void addMacOSAdditions(std::vector<MappedMachOsByCategory>& allFileSets)
{
+ dyld3::closure::FileSystemPhysical fileSystem;
for (const char* addPath : sMacOsAdditions) {
struct stat statBuf;
- if ( stat(addPath, &statBuf) == 0 )
- addIfMachO("", addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+ if ( stat(addPath, &statBuf) == 0 ) {
+ addIfMachO(fileSystem, addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+ }
}
}
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();
+ const char* installName = aFile.mh->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;
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();
+ const char* installName = aFile.mh->installName();
auto pos = installNameToFirstPath.find(installName);
if ( pos == installNameToFirstPath.end() ) {
installNameToFirstPath[installName] = aFile.runtimePath;
verbose = true;
}
else if (strcmp(arg, "-tvOS") == 0) {
- platform = dyld3::Platform::tvOS;
+ platform = dyld3::Platform::tvOS_simulator;
}
else if (strcmp(arg, "-iOS") == 0) {
- platform = dyld3::Platform::iOS;
+ platform = dyld3::Platform::iOS_simulator;
}
else if (strcmp(arg, "-watchOS") == 0) {
- platform = dyld3::Platform::watchOS;
+ platform = dyld3::Platform::watchOS_simulator;
}
else if ( strcmp(arg, "-runtime_dir") == 0 ) {
TERMINATE_IF_LAST_ARG("-runtime_dir missing path argument\n");
if ( archStrs.empty() ) {
switch ( platform ) {
- case dyld3::Platform::iOS:
+ case dyld3::Platform::iOS_simulator:
archStrs.insert("x86_64");
break;
- case dyld3::Platform::tvOS:
+ case dyld3::Platform::tvOS_simulator:
archStrs.insert("x86_64");
break;
- case dyld3::Platform::watchOS:
+ case dyld3::Platform::watchOS_simulator:
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;
+ case dyld3::Platform::iOS:
+ case dyld3::Platform::tvOS:
+ case dyld3::Platform::watchOS:
+ case dyld3::Platform::iOSMac:
+ case dyld3::Platform::unknown:
+ assert(0 && "invalid platform");
+ break;
}
}
if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
std::vector<MappedMachOsByCategory> mappedFiles;
mappedFiles.push_back({fileSet.archName});
- if ( addIfMachO(rootPath, runtimePath, statBuf, platform, mappedFiles) ) {
+ dyld3::closure::FileSystemPhysical fileSystem(rootPath.c_str());
+ if ( addIfMachO(fileSystem, runtimePath, statBuf, platform, mappedFiles) ) {
if ( !mappedFiles.back().dylibsForCache.empty() )
return mappedFiles.back().dylibsForCache.back();
}
// build cache new cache file
DyldSharedCache::CreateOptions options;
+ options.outputFilePath = outFile;
+ options.outputMapFilePath = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
options.archName = fileSet.archName;
options.platform = platform;
options.excludeLocalSymbols = false;
options.inodesAreSameAsRuntime = true;
options.cacheSupportsASLR = false;
options.forSimulator = true;
+ options.isLocallyBuiltCache = true;
options.verbose = verbose;
options.evictLeafDylibsOnOverflow = true;
options.pathPrefixes = { rootPath };
// 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());
+ 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_sim_shared_cache: %s\n", results.errorMessage.c_str());
+ 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);
- }
- // 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
* 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
+extern void _tlv_bootstrap(void) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
/*
* The following dyld API's are deprecated as of Mac OS X 10.5. They are either
NSObjectFileImageAccess
} NSObjectFileImageReturnCode;
-typedef struct __NSObjectFileImage* NSObjectFileImage;
+typedef struct __NSObjectFileImage* NSObjectFileImage;
/* NSObjectFileImage can only be used with MH_BUNDLE files */
-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 NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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()");
+extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()");
typedef struct __NSModule* NSModule;
-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 const char* NSNameOfModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char* NSLibraryNameForModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
-extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSUnLinkModule(NSModule module, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __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()");
+extern bool NSIsSymbolNameDefined(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __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()");
+extern const char* NSNameOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern void * NSAddressOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSModule NSModuleForSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()");
+extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
-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()");
+extern bool NSAddLibrary(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern bool NSAddLibraryWithSearching(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __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 bool _dyld_present(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "always true");
+extern bool _dyld_launched_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "moot");
+extern bool _dyld_all_twolevel_modules_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "moot");
+extern bool _dyld_bind_fully_image_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)");
+extern bool _dyld_image_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
+extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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()");
+extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
#if __cplusplus
#include <stdbool.h>
#include <unistd.h>
#include <mach/mach.h>
+#include <uuid/uuid.h>
+
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+#include <atomic>
+#endif
#ifdef __cplusplus
extern "C" {
struct dyld_all_image_infos {
uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */
uint32_t infoArrayCount;
- const struct dyld_image_info* infoArray;
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+ std::atomic<const struct dyld_image_info*> infoArray;
+#else
+ const struct dyld_image_info* infoArray;
+#endif
dyld_image_notifier notification;
bool processDetachedFromSharedRegion;
/* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */
uint8_t sharedCacheUUID[16];
/* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */
uintptr_t sharedCacheBaseAddress;
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+ // We want this to be atomic in libdyld so that we can see updates when we map it shared
+ std::atomic<uint64_t> infoArrayChangeTimestamp;
+#else
uint64_t infoArrayChangeTimestamp;
+#endif
const char* dyldPath;
mach_port_t notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
#if __LP64__
#ifndef _MACH_O_DYLD_PRIV_H_
#define _MACH_O_DYLD_PRIV_H_
+#include <assert.h>
#include <stdbool.h>
#include <Availability.h>
#include <TargetConditionals.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
+#include <uuid/uuid.h>
#if __cplusplus
extern "C" {
//
// private interface between libSystem.dylib and dyld
//
-extern void _dyld_fork_child();
+extern void _dyld_fork_child(void);
// DEPRECATED
// Exists in Mac OS X 10.11 and later
extern const struct mach_header* dyld_image_header_containing_address(const void* addr);
+typedef uint32_t dyld_platform_t;
+typedef struct {
+ dyld_platform_t platform;
+ uint32_t version;
+} dyld_build_version_t;
+
+// Returns the active platform of the process
+extern dyld_platform_t dyld_get_active_platform(void) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Base platforms are platforms that have version numbers (macOS, iOS, watchos, tvOS, bridgeOS)
+// All other platforms are mapped to a base platform for version checks
+extern dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// SPI to ask if a platform is a simulation platform
+extern bool dyld_is_simulator_platform(dyld_platform_t platform) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Takes a version and returns if the image was built againt that SDK or newer
+// In the case of multi_plaform mach-o's it tests against the active platform
+extern bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Takes a version and returns if the image was built with that minos version or newer
+// In the case of multi_plaform mach-o's it tests against the active platform
+extern bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Convenience versions of the previous two functions that run against the the main executable
+extern bool dyld_program_sdk_at_least(dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+extern bool dyld_program_minos_at_least(dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Function that walks through the load commands and calls the internal block for every version found
+// Intended as a fallback for very complex (and rare) version checks, or for tools that need to
+// print our everything for diagnostic reasons
+extern void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Convienence constants for dyld version SPIs.
+
+//@VERSION_SET_DEFS@
+
+//@MACOS_PLATFORM_VERSION_DEFS@
+
+//@IOS_PLATFORM_VERSION_DEFS@
+
+//@WATCHOS_PLATFORM_VERSION_DEFS@
+
+//@TVOS_PLATFORM_VERSION_DEFS@
+
+//@BRIDGEOS_PLATFORM_VERSION_DEFS@
// Convienence constants for return values from dyld_get_sdk_version() and friends.
//@WATCHOS_VERSION_DEFS@
-
//
// This finds the SDK version a binary was built against.
// Returns zero on error, or if SDK version could not be determined.
//
// Exists in Mac OS X 10.8 and later
// Exists in iOS 6.0 and later
-extern uint32_t dyld_get_program_sdk_version();
-
+extern uint32_t dyld_get_program_sdk_version(void);
-#if __WATCH_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_WATCH
// 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() __IOS_UNAVAILABLE __OSX_UNAVAILABLE __WATCHOS_AVAILABLE(2.0);
+extern uint32_t dyld_get_program_sdk_watch_os_version(void) __API_AVAILABLE(watchos(2.0));
// watchOS only.
// 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);
+extern uint32_t dyld_get_program_min_watch_os_version(void) __API_AVAILABLE(watchos(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();
+extern uint32_t dyld_get_program_sdk_bridge_os_version(void) __API_AVAILABLE(bridgeos(2.0));
// 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();
+extern uint32_t dyld_get_program_min_bridge_os_version(void) __API_AVAILABLE(bridgeos(2.0));
#endif
//
//
// Exists in Mac OS X 10.8 and later
// Exists in iOS 6.0 and later
-extern uint32_t dyld_get_program_min_os_version();
+extern uint32_t dyld_get_program_min_os_version(void);
//
// Exists in iPhoneOS 3.1 and later
// Exists in Mac OS X 10.10 and later
-extern bool dyld_shared_cache_some_image_overridden();
+extern bool dyld_shared_cache_some_image_overridden(void);
// Returns if the process is setuid or is code signed with entitlements.
//
// Exists in Mac OS X 10.9 and later
-extern bool dyld_process_is_restricted();
+extern bool dyld_process_is_restricted(void);
// Returns path used by dyld for standard dyld shared cache file for the current arch.
//
// Exists in Mac OS X 10.11 and later
-extern const char* dyld_shared_cache_file_path();
+extern const char* dyld_shared_cache_file_path(void);
+
+struct dyld_image_uuid_offset {
+ uuid_t uuid;
+ uint64_t offsetInImage;
+ const struct mach_header* image;
+};
+
+//
+// Given an array of addresses, returns info about each address.
+// Common usage is the array or addresses was produced by a stack backtrace.
+// For each address, returns the where that image was loaded, the offset
+// of the address in the image, and the image's uuid. If a specified
+// address is unknown to dyld, all fields will be returned a zeros.
+//
+// Exists in macOS 10.14 and later
+// Exists in iOS 12.0 and later
+extern void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]);
+
+
+//
+// Lets you register a callback which is called each time an image is loaded and provides the mach_header*, path, and
+// whether the image may be unloaded later. During the call to _dyld_register_for_image_loads(), the callback is called
+// once for each image currently loaded.
+//
+// Exists in macOS 10.14 and later
+// Exists in iOS 12.0 and later
+extern void _dyld_register_for_image_loads(void (*func)(const struct mach_header* mh, const char* path, bool unloadable));
+
+
+
//
// When dyld must terminate a process because of a required dependent dylib
// could not be loaded or a symbol is missing, dyld calls abort_with_reason()
// called by libSystem_initializer only
-extern void _dyld_initializer();
+extern void _dyld_initializer(void);
// never called from source code. Used by static linker to implement lazy binding
-extern void dyld_stub_binder() __asm__("dyld_stub_binder");
+extern void dyld_stub_binder(void) __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();
+extern void _tlv_exit(void);
// temp exports to keep tapi happy, until ASan stops using dyldVersionNumber
#include <unistd.h>
#include <mach/mach.h>
#include <dispatch/dispatch.h>
+#include <uuid/uuid.h>
#ifdef __cplusplus
extern "C" {
//
extern dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
- void (^notifyExit)(),
+ void (^notifyExit)(void),
kern_return_t* kernelError);
// add block to call right before main() is entered.
// does nothing if process is already in main().
-extern void _dyld_process_info_notify_main(dyld_process_info_notify objc, void (^notifyMain)());
+extern void _dyld_process_info_notify_main(dyld_process_info_notify objc, void (^notifyMain)(void));
// stop notifications and invalid dyld_process_info_notify object
objc_stringhash_offset_t *o;
o = protocolOffsets();
- for (objc_stringhash_offset_t i = 0; i < capacity; i++) {
+ for (objc_stringhash_offset_t i = 0; i < (int)capacity; i++) {
S32(o[i]);
}
// gencode *final; /* output, code for the final hash */
{
ub4 loga = log2u(alen); /* log based 2 of blen */
- ub4 i;
- for (i = 0; i < nkeys; i++) {
+ dispatch_apply(nkeys, DISPATCH_APPLY_AUTO, ^(size_t index) {
+ ub4 i = (ub4)index;
key *mykey = keys+i;
ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt);
mykey->a_k = (loga > 0) ? (ub4)(hash >> (UB8BITS-loga)) : 0;
mykey->b_k = (blen > 1) ? (hash & (blen-1)) : 0;
- }
+ });
}
ub8 salt; /* a parameter to the hash function */
ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */
int ok;
- int i;
+ uint32_t i;
perfect_hash result;
/* read in the list of keywords */
};
+struct arm64_32
+{
+ typedef Pointer32<LittleEndian> P;
+
+};
+
};
+template <typename E>
+class dyldCacheSlideInfo3 {
+public:
+ uint32_t version() const INLINE { return E::get32(fields.version); }
+ void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
+
+ uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); }
+ void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); }
+
+ uint32_t page_size() const INLINE { return E::get32(fields.page_size); }
+ void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); }
+
+ uint64_t auth_value_add() const INLINE { return E::get64(fields.auth_value_add); }
+ void set_auth_value_add(uint64_t value) INLINE { E::set64(fields.auth_value_add, value); }
+
+ uint16_t page_starts(unsigned index) const INLINE { return E::get16(fields.page_starts[index]); }
+ void set_page_starts(unsigned index, uint16_t value) INLINE { E::set16(fields.page_starts[index], value); }
+
+
+private:
+ dyld_cache_slide_info3 fields;
+};
+
+
template <typename E>
class dyldCacheLocalSymbolsInfo {
#define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64))
#endif
+#ifndef CPU_ARCH_ABI64_32
+ #define CPU_ARCH_ABI64_32 ((cpu_type_t) 0x02000000)
+#endif
+#ifndef CPU_TYPE_ARM64_32
+ #define CPU_TYPE_ARM64_32 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64_32))
+#endif
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+ #define CPU_SUBTYPE_ARM64_32_V8 ((cpu_subtype_t) 1)
+#endif
+
+
#define ARM64_RELOC_UNSIGNED 0 // for pointers
} while (byte & 0x80);
// sign extend negative numbers
if ( (byte & 0x40) != 0 )
- result |= (-1LL) << bit;
+ result |= (~0ULL) << bit;
return result;
}
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 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,
* 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 <mach-o/loader.h>
#include <Availability.h>
-#define NO_ULEB
+#include "CodeSigningTypes.h"
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#define NO_ULEB
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
#include "CacheFileAbstraction.hpp"
#include "dsc_iterator.h"
#include "dsc_extractor.h"
#include "MachOTrie.hpp"
+#include "SupportedArchs.h"
+#include "DyldSharedCache.h"
#include <vector>
#include <set>
struct seg_info
{
- seg_info(const char* n, uint64_t o, uint64_t s)
- : segName(n), offset(o), sizem(s) { }
- const char* segName;
- uint64_t offset;
- uint64_t sizem;
+ seg_info(const char* n, uint64_t o, uint64_t s)
+ : segName(n), offset(o), sizem(s) { }
+ const char* segName;
+ uint64_t offset;
+ uint64_t sizem;
};
class CStringHash {
public:
- size_t operator()(const char* __s) const {
- size_t __h = 0;
- for ( ; *__s; ++__s)
- __h = 5 * __h + *__s;
- return __h;
- };
+ size_t operator()(const char* __s) const {
+ size_t __h = 0;
+ for ( ; *__s; ++__s)
+ __h = 5 * __h + *__s;
+ return __h;
+ };
};
class CStringEquals {
public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
};
typedef std::unordered_map<const char*, std::vector<seg_info>, CStringHash, CStringEquals> NameToSegments;
// Filter to find individual symbol re-exports in trie
class NotReExportSymbol {
public:
- NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
- bool operator()(const mach_o::trie::Entry &entry) const {
- bool result = isSymbolReExport(entry);
- if (result) {
- // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
- ::free((void*)entry.name);
- const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
- }
- return result;
- }
+ NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
+ bool operator()(const mach_o::trie::Entry &entry) const {
+ bool result = isSymbolReExport(entry);
+ if (result) {
+ // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+ ::free((void*)entry.name);
+ const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
+ }
+ return result;
+ }
private:
- bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
- if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
- return true;
- if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
- return true;
- // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
- if ( _reexportDeps.count((int)entry.other) != 0 )
- return true;
- return false;
- }
- const std::set<int> &_reexportDeps;
+ bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
+ if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
+ return true;
+ if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
+ return true;
+ // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
+ if ( _reexportDeps.count((int)entry.other) != 0 )
+ return true;
+ return false;
+ }
+ const std::set<int> &_reexportDeps;
};
+template <typename P>
+struct LoadCommandInfo {
+};
template <typename A>
-int optimize_linkedit(macho_header<typename A::P>* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize)
-{
- typedef typename A::P P;
- typedef typename A::P::E E;
+class LinkeditOptimizer {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
- // update header flags
- mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
-
- // update load commands
- uint64_t cumulativeFileSize = 0;
- const unsigned origLoadCommandsSize = mh->sizeofcmds();
- unsigned bytesRemaining = origLoadCommandsSize;
- unsigned removedCount = 0;
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
- const uint32_t cmdCount = mh->ncmds();
- const macho_load_command<P>* cmd = cmds;
- macho_segment_command<P>* linkEditSegCmd = NULL;
- macho_symtab_command<P>* symtab = NULL;
- macho_dysymtab_command<P>* dynamicSymTab = NULL;
- macho_linkedit_data_command<P>* functionStarts = NULL;
- macho_linkedit_data_command<P>* dataInCode = NULL;
- uint32_t exportsTrieOffset = 0;
- uint32_t exportsTrieSize = 0;
- std::set<int> reexportDeps;
- int depIndex = 0;
- for (uint32_t i = 0; i < cmdCount; ++i) {
- bool remove = false;
- switch ( cmd->cmd() ) {
- case macho_segment_command<P>::CMD:
- {
- // update segment/section file offsets
- macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- segCmd->set_fileoff(cumulativeFileSize);
- macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
- macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
- for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( sect->offset() != 0 )
- sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr()));
- }
- if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
- linkEditSegCmd = segCmd;
- }
- cumulativeFileSize += segCmd->filesize();
- }
- break;
- case LC_DYLD_INFO_ONLY:
- {
- // zero out all dyld info
- macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
- exportsTrieOffset = dyldInfo->export_off();
- exportsTrieSize = dyldInfo->export_size();
- dyldInfo->set_rebase_off(0);
- dyldInfo->set_rebase_size(0);
- dyldInfo->set_bind_off(0);
- dyldInfo->set_bind_size(0);
- dyldInfo->set_weak_bind_off(0);
- dyldInfo->set_weak_bind_size(0);
- dyldInfo->set_lazy_bind_off(0);
- dyldInfo->set_lazy_bind_size(0);
- dyldInfo->set_export_off(0);
- dyldInfo->set_export_size(0);
- }
- break;
- case LC_SYMTAB:
- symtab = (macho_symtab_command<P>*)cmd;
- break;
- case LC_DYSYMTAB:
- dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
- break;
- case LC_FUNCTION_STARTS:
- functionStarts = (macho_linkedit_data_command<P>*)cmd;
- break;
- case LC_DATA_IN_CODE:
- dataInCode = (macho_linkedit_data_command<P>*)cmd;
- break;
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB:
- ++depIndex;
- if ( cmd->cmd() == LC_REEXPORT_DYLIB ) {
- reexportDeps.insert(depIndex);
- }
- break;
- case LC_SEGMENT_SPLIT_INFO:
- // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO
- remove = true;
- break;
- }
- uint32_t cmdSize = cmd->cmdsize();
- macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
- if ( remove ) {
- ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
- ++removedCount;
- }
- else {
- bytesRemaining -= cmdSize;
- cmd = nextCmd;
- }
- }
- // zero out stuff removed
- ::bzero((void*)cmd, bytesRemaining);
- // update header
- mh->set_ncmds(cmdCount - removedCount);
- mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
-
- // rebuild symbol table
- if ( linkEditSegCmd == NULL ) {
- fprintf(stderr, "__LINKEDIT not found\n");
- return -1;
- }
- if ( symtab == NULL ) {
- fprintf(stderr, "LC_SYMTAB not found\n");
- return -1;
- }
- if ( dynamicSymTab == NULL ) {
- fprintf(stderr, "LC_DYSYMTAB not found\n");
- return -1;
- }
-
- const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff();
- uint32_t functionStartsSize = 0;
- if ( functionStarts != NULL ) {
- // copy function starts from original cache file to new mapped dylib file
- functionStartsSize = functionStarts->datasize();
- memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize);
- }
- const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
- uint32_t dataInCodeSize = 0;
- if ( dataInCode != NULL ) {
- // copy data-in-code info from original cache file to new mapped dylib file
- dataInCodeSize = dataInCode->datasize();
- memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize);
- }
-
- std::vector<mach_o::trie::Entry> exports;
- if ( exportsTrieSize != 0 ) {
- const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset;
- const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
- mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
- exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
- }
-
- // look for local symbol info in unmapped part of shared cache
- dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)mapped_cache;
- macho_nlist<P>* localNlists = NULL;
- uint32_t localNlistCount = 0;
- const char* localStrings = NULL;
- const char* localStringsEnd = NULL;
- if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) {
- dyldCacheLocalSymbolsInfo<E>* localInfo = (dyldCacheLocalSymbolsInfo<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset());
- dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset());
- macho_nlist<P>* allLocalNlists = (macho_nlist<P>*)(((uint8_t*)localInfo) + localInfo->nlistOffset());
- const uint32_t entriesCount = localInfo->entriesCount();
- for (uint32_t i=0; i < entriesCount; ++i) {
- if ( entries[i].dylibOffset() == textOffsetInCache ) {
- uint32_t localNlistStart = entries[i].nlistStartIndex();
- localNlistCount = entries[i].nlistCount();
- localNlists = &allLocalNlists[localNlistStart];
- localStrings = ((char*)localInfo) + localInfo->stringsOffset();
- localStringsEnd = &localStrings[localInfo->stringsSize()];
- break;
- }
- }
- }
- // compute number of symbols in new symbol table
- const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
- const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
- uint32_t newSymCount = symtab->nsyms();
- if ( localNlists != NULL ) {
- newSymCount = localNlistCount;
- for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
- // skip any locals in cache
- if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT )
- continue;
- ++newSymCount;
- }
- }
-
- // add room for N_INDR symbols for re-exported symbols
- newSymCount += exports.size();
-
- // copy symbol entries and strings from original cache file to new mapped dylib file
- const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
- const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist<P>);
- const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
- macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
- char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
- const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
- const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
- const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()];
- macho_nlist<P>* t = newSymTabStart;
- int poolOffset = 0;
- uint32_t symbolsCopied = 0;
- newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string
- for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
- // if we have better local symbol info, skip any locals here
- if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) )
- continue;
- *t = *s;
- t->set_n_strx(poolOffset);
- const char* symName = &mergedStringPoolStart[s->n_strx()];
- if ( symName > mergedStringPoolEnd )
- symName = "<corrupt symbol name>";
- strcpy(&newStringPoolStart[poolOffset], symName);
- poolOffset += (strlen(symName) + 1);
- ++t;
- ++symbolsCopied;
- }
- // <rdar://problem/16529213> recreate N_INDR symbols in extracted dylibs for debugger
- for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
- strcpy(&newStringPoolStart[poolOffset], it->name);
- t->set_n_strx(poolOffset);
- poolOffset += (strlen(it->name) + 1);
- t->set_n_type(N_INDR | N_EXT);
- t->set_n_sect(0);
- t->set_n_desc(0);
- const char* importName = it->importName;
- if ( *importName == '\0' )
- importName = it->name;
- strcpy(&newStringPoolStart[poolOffset], importName);
- t->set_n_value(poolOffset);
- poolOffset += (strlen(importName) + 1);
- ++t;
- ++symbolsCopied;
- }
- if ( localNlists != NULL ) {
- // update load command to reflect new count of locals
- dynamicSymTab->set_ilocalsym(symbolsCopied);
- dynamicSymTab->set_nlocalsym(localNlistCount);
- // copy local symbols
- for (uint32_t i=0; i < localNlistCount; ++i) {
- const char* localName = &localStrings[localNlists[i].n_strx()];
- if ( localName > localStringsEnd )
- localName = "<corrupt local symbol name>";
- *t = localNlists[i];
- t->set_n_strx(poolOffset);
- strcpy(&newStringPoolStart[poolOffset], localName);
- poolOffset += (strlen(localName) + 1);
- ++t;
- ++symbolsCopied;
- }
- }
-
- if ( newSymCount != symbolsCopied ) {
- fprintf(stderr, "symbol count miscalculation\n");
- return -1;
- }
-
- // pointer align string pool size
- while ( (poolOffset % sizeof(pint_t)) != 0 )
- ++poolOffset;
- // copy indirect symbol table
- uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset);
- memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t));
-
- // update load commands
- if ( functionStarts != NULL ) {
- functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset);
- functionStarts->set_datasize(functionStartsSize);
- }
- if ( dataInCode != NULL ) {
- dataInCode->set_dataoff((uint32_t)newDataInCodeOffset);
- dataInCode->set_datasize(dataInCodeSize);
- }
- symtab->set_nsyms(symbolsCopied);
- symtab->set_symoff((uint32_t)newSymTabOffset);
- symtab->set_stroff((uint32_t)newStringPoolOffset);
- symtab->set_strsize(poolOffset);
- dynamicSymTab->set_extreloff(0);
- dynamicSymTab->set_nextrel(0);
- dynamicSymTab->set_locreloff(0);
- dynamicSymTab->set_nlocrel(0);
- dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset);
- linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
- linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
-
- // return new size
- *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096);
-
- // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
- for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
- ::free((void*)(it->name));
- }
-
-
- return 0;
-}
+private:
+ macho_segment_command<P>* linkEditSegCmd = NULL;
+ macho_symtab_command<P>* symtab = NULL;
+ macho_dysymtab_command<P>* dynamicSymTab = NULL;
+ macho_linkedit_data_command<P>* functionStarts = NULL;
+ macho_linkedit_data_command<P>* dataInCode = NULL;
+ uint32_t exportsTrieOffset = 0;
+ uint32_t exportsTrieSize = 0;
+ std::set<int> reexportDeps;
+
+public:
+
+ void optimize_loadcommands(macho_header<typename A::P>* mh)
+ {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ // update header flags
+ mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
+
+ // update load commands
+ uint64_t cumulativeFileSize = 0;
+ const unsigned origLoadCommandsSize = mh->sizeofcmds();
+ unsigned bytesRemaining = origLoadCommandsSize;
+ unsigned removedCount = 0;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmdCount = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ int depIndex = 0;
+ for (uint32_t i = 0; i < cmdCount; ++i) {
+ bool remove = false;
+ switch ( cmd->cmd() ) {
+ case macho_segment_command<P>::CMD:
+ {
+ // update segment/section file offsets
+ macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ segCmd->set_fileoff(cumulativeFileSize);
+ segCmd->set_filesize(segCmd->vmsize());
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->offset() != 0 )
+ sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr()));
+ }
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ linkEditSegCmd = segCmd;
+ }
+ cumulativeFileSize += segCmd->filesize();
+ break;
+ }
+ case LC_DYLD_INFO_ONLY:
+ {
+ // zero out all dyld info
+ macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ exportsTrieOffset = dyldInfo->export_off();
+ exportsTrieSize = dyldInfo->export_size();
+ dyldInfo->set_rebase_off(0);
+ dyldInfo->set_rebase_size(0);
+ dyldInfo->set_bind_off(0);
+ dyldInfo->set_bind_size(0);
+ dyldInfo->set_weak_bind_off(0);
+ dyldInfo->set_weak_bind_size(0);
+ dyldInfo->set_lazy_bind_off(0);
+ dyldInfo->set_lazy_bind_size(0);
+ dyldInfo->set_export_off(0);
+ dyldInfo->set_export_size(0);
+ }
+ break;
+ case LC_SYMTAB:
+ symtab = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_FUNCTION_STARTS:
+ functionStarts = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DATA_IN_CODE:
+ dataInCode = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ ++depIndex;
+ if ( cmd->cmd() == LC_REEXPORT_DYLIB ) {
+ reexportDeps.insert(depIndex);
+ }
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO
+ remove = true;
+ break;
+ }
+ uint32_t cmdSize = cmd->cmdsize();
+ macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
+ if ( remove ) {
+ ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+ ++removedCount;
+ }
+ else {
+ bytesRemaining -= cmdSize;
+ cmd = nextCmd;
+ }
+ }
+ // zero out stuff removed
+ ::bzero((void*)cmd, bytesRemaining);
+ // update header
+ mh->set_ncmds(cmdCount - removedCount);
+ mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
+ }
+ int optimize_linkedit(std::vector<uint8_t> &new_linkedit_data, uint64_t textOffsetInCache, const void* mapped_cache)
+ {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ // rebuild symbol table
+ if ( linkEditSegCmd == NULL ) {
+ fprintf(stderr, "__LINKEDIT not found\n");
+ return -1;
+ }
+ if ( symtab == NULL ) {
+ fprintf(stderr, "LC_SYMTAB not found\n");
+ return -1;
+ }
+ if ( dynamicSymTab == NULL ) {
+ fprintf(stderr, "LC_DYSYMTAB not found\n");
+ return -1;
+ }
+
+ const uint64_t newFunctionStartsOffset = new_linkedit_data.size();
+ uint32_t functionStartsSize = 0;
+ if ( functionStarts != NULL ) {
+ // copy function starts from original cache file to new mapped dylib file
+ functionStartsSize = functionStarts->datasize();
+ new_linkedit_data.insert(new_linkedit_data.end(),
+ (char*)mapped_cache + functionStarts->dataoff(),
+ (char*)mapped_cache + functionStarts->dataoff() + functionStartsSize);
+ }
+
+ // pointer align
+ while ((linkEditSegCmd->fileoff() + new_linkedit_data.size()) % sizeof(pint_t))
+ new_linkedit_data.push_back(0);
+
+ const uint64_t newDataInCodeOffset = new_linkedit_data.size();
+ uint32_t dataInCodeSize = 0;
+ if ( dataInCode != NULL ) {
+ // copy data-in-code info from original cache file to new mapped dylib file
+ dataInCodeSize = dataInCode->datasize();
+ new_linkedit_data.insert(new_linkedit_data.end(),
+ (char*)mapped_cache + dataInCode->dataoff(),
+ (char*)mapped_cache + dataInCode->dataoff() + dataInCodeSize);
+ }
+
+ std::vector<mach_o::trie::Entry> exports;
+ if ( exportsTrieSize != 0 ) {
+ const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset;
+ const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
+ mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
+ exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
+ }
+
+ // look for local symbol info in unmapped part of shared cache
+ dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)mapped_cache;
+ macho_nlist<P>* localNlists = NULL;
+ uint32_t localNlistCount = 0;
+ const char* localStrings = NULL;
+ const char* localStringsEnd = NULL;
+ if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) {
+ dyldCacheLocalSymbolsInfo<E>* localInfo = (dyldCacheLocalSymbolsInfo<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset());
+ dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset());
+ macho_nlist<P>* allLocalNlists = (macho_nlist<P>*)(((uint8_t*)localInfo) + localInfo->nlistOffset());
+ const uint32_t entriesCount = localInfo->entriesCount();
+ for (uint32_t i=0; i < entriesCount; ++i) {
+ if ( entries[i].dylibOffset() == textOffsetInCache ) {
+ uint32_t localNlistStart = entries[i].nlistStartIndex();
+ localNlistCount = entries[i].nlistCount();
+ localNlists = &allLocalNlists[localNlistStart];
+ localStrings = ((char*)localInfo) + localInfo->stringsOffset();
+ localStringsEnd = &localStrings[localInfo->stringsSize()];
+ break;
+ }
+ }
+ }
+ // compute number of symbols in new symbol table
+ const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
+ const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
+ uint32_t newSymCount = symtab->nsyms();
+ if ( localNlists != NULL ) {
+ newSymCount = localNlistCount;
+ for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
+ // skip any locals in cache
+ if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT )
+ continue;
+ ++newSymCount;
+ }
+ }
+
+ // add room for N_INDR symbols for re-exported symbols
+ newSymCount += exports.size();
+
+ // copy symbol entries and strings from original cache file to new mapped dylib file
+ const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
+ const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()];
+
+ // First count how many entries we need
+ std::vector<macho_nlist<P>> newSymTab;
+ newSymTab.reserve(newSymCount);
+ std::vector<char> newSymNames;
+
+ // first pool entry is always empty string
+ newSymNames.push_back('\0');
+
+ for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
+ // if we have better local symbol info, skip any locals here
+ if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) )
+ continue;
+ macho_nlist<P> t = *s;
+ t.set_n_strx((uint32_t)newSymNames.size());
+ const char* symName = &mergedStringPoolStart[s->n_strx()];
+ if ( symName > mergedStringPoolEnd )
+ symName = "<corrupt symbol name>";
+ newSymNames.insert(newSymNames.end(),
+ symName,
+ symName + (strlen(symName) + 1));
+ newSymTab.push_back(t);
+ }
+ // <rdar://problem/16529213> recreate N_INDR symbols in extracted dylibs for debugger
+ for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+ macho_nlist<P> t;
+ memset(&t, 0, sizeof(t));
+ t.set_n_strx((uint32_t)newSymNames.size());
+ t.set_n_type(N_INDR | N_EXT);
+ t.set_n_sect(0);
+ t.set_n_desc(0);
+ newSymNames.insert(newSymNames.end(),
+ it->name,
+ it->name + (strlen(it->name) + 1));
+ const char* importName = it->importName;
+ if ( *importName == '\0' )
+ importName = it->name;
+ t.set_n_value(newSymNames.size());
+ newSymNames.insert(newSymNames.end(),
+ importName,
+ importName + (strlen(importName) + 1));
+ newSymTab.push_back(t);
+ }
+ if ( localNlists != NULL ) {
+ // update load command to reflect new count of locals
+ dynamicSymTab->set_ilocalsym((uint32_t)newSymTab.size());
+ dynamicSymTab->set_nlocalsym(localNlistCount);
+ // copy local symbols
+ for (uint32_t i=0; i < localNlistCount; ++i) {
+ const char* localName = &localStrings[localNlists[i].n_strx()];
+ if ( localName > localStringsEnd )
+ localName = "<corrupt local symbol name>";
+ macho_nlist<P> t = localNlists[i];
+ t.set_n_strx((uint32_t)newSymNames.size());
+ newSymNames.insert(newSymNames.end(),
+ localName,
+ localName + (strlen(localName) + 1));
+ newSymTab.push_back(t);
+ }
+ }
+
+ if ( newSymCount != newSymTab.size() ) {
+ fprintf(stderr, "symbol count miscalculation\n");
+ return -1;
+ }
+
+ //const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
+ //macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
+ //char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
+
+ // pointer align
+ while ((linkEditSegCmd->fileoff() + new_linkedit_data.size()) % sizeof(pint_t))
+ new_linkedit_data.push_back(0);
+
+ const uint64_t newSymTabOffset = new_linkedit_data.size();
+
+ // Copy sym tab
+ for (macho_nlist<P>& sym : newSymTab) {
+ uint8_t symData[sizeof(macho_nlist<P>)];
+ memcpy(&symData, &sym, sizeof(sym));
+ new_linkedit_data.insert(new_linkedit_data.end(), &symData[0], &symData[sizeof(macho_nlist<P>)]);
+ }
+
+ const uint64_t newIndSymTabOffset = new_linkedit_data.size();
+
+ // Copy indirect symbol table
+ const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
+ new_linkedit_data.insert(new_linkedit_data.end(),
+ (char*)mergedIndSymTab,
+ (char*)(mergedIndSymTab + dynamicSymTab->nindirectsyms()));
+
+ const uint64_t newStringPoolOffset = new_linkedit_data.size();
+
+ // pointer align string pool size
+ while (newSymNames.size() % sizeof(pint_t))
+ newSymNames.push_back('\0');
+
+ new_linkedit_data.insert(new_linkedit_data.end(), newSymNames.begin(), newSymNames.end());
+
+ // update load commands
+ if ( functionStarts != NULL ) {
+ functionStarts->set_dataoff((uint32_t)(newFunctionStartsOffset + linkEditSegCmd->fileoff()));
+ functionStarts->set_datasize(functionStartsSize);
+ }
+ if ( dataInCode != NULL ) {
+ dataInCode->set_dataoff((uint32_t)(newDataInCodeOffset + linkEditSegCmd->fileoff()));
+ dataInCode->set_datasize(dataInCodeSize);
+ }
+
+ symtab->set_nsyms(newSymCount);
+ symtab->set_symoff((uint32_t)(newSymTabOffset + linkEditSegCmd->fileoff()));
+ symtab->set_stroff((uint32_t)(newStringPoolOffset + linkEditSegCmd->fileoff()));
+ symtab->set_strsize((uint32_t)newSymNames.size());
+ dynamicSymTab->set_extreloff(0);
+ dynamicSymTab->set_nextrel(0);
+ dynamicSymTab->set_locreloff(0);
+ dynamicSymTab->set_nlocrel(0);
+ dynamicSymTab->set_indirectsymoff((uint32_t)(newIndSymTabOffset + linkEditSegCmd->fileoff()));
+ linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
+ linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
+
+ // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+ for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+ ::free((void*)(it->name));
+ }
+
+
+ return 0;
+ }
+};
-static void make_dirs(const char* file_path)
+static void make_dirs(const char* file_path)
{
- //printf("make_dirs(%s)\n", file_path);
- char dirs[strlen(file_path)+1];
- strcpy(dirs, file_path);
- char* lastSlash = strrchr(dirs, '/');
- if ( lastSlash == NULL )
- return;
- lastSlash[1] = '\0';
- struct stat stat_buf;
- if ( stat(dirs, &stat_buf) != 0 ) {
- char* afterSlash = &dirs[1];
- char* slash;
- while ( (slash = strchr(afterSlash, '/')) != NULL ) {
- *slash = '\0';
- ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
- //printf("mkdir(%s)\n", dirs);
- *slash = '/';
- afterSlash = slash+1;
- }
- }
+ //printf("make_dirs(%s)\n", file_path);
+ char dirs[strlen(file_path)+1];
+ strcpy(dirs, file_path);
+ char* lastSlash = strrchr(dirs, '/');
+ if ( lastSlash == NULL )
+ return;
+ lastSlash[1] = '\0';
+ struct stat stat_buf;
+ if ( stat(dirs, &stat_buf) != 0 ) {
+ char* afterSlash = &dirs[1];
+ char* slash;
+ while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+ *slash = '\0';
+ ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ //printf("mkdir(%s)\n", dirs);
+ *slash = '/';
+ afterSlash = slash+1;
+ }
+ }
}
template <typename A>
-size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {
- typedef typename A::P P;
-
- size_t additionalSize = 0;
- for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
- additionalSize += it->sizem;
- }
-
- dylib_data.reserve(dylib_data.size() + additionalSize);
-
- uint32_t nfat_archs = 0;
- uint32_t offsetInFatFile = 4096;
+size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {
+ typedef typename A::P P;
+
+ int32_t nfat_archs = 0;
+ uint32_t offsetInFatFile = 4096;
uint8_t *base_ptr = &dylib_data.front();
-
+
#define FH reinterpret_cast<fat_header*>(base_ptr)
#define FA reinterpret_cast<fat_arch*>(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch)))
-
+
if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) {
- // have fat header, append new arch to end
+ // have fat header, append new arch to end
nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch);
- offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
+ offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
}
-
- dylib_data.resize(offsetInFatFile);
- base_ptr = &dylib_data.front();
-
- FH->magic = OSSwapHostToBigInt32(FAT_MAGIC);
+
+ // First see if this slice already exists.
+ for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+ if(strcmp(it->segName, "__TEXT") == 0 ) {
+ const macho_header<P> *textMH = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+it->offset);
+
+ // if this cputype/subtype already exist in fat header, then return immediately
+ for(int32_t i=0; i < nfat_archs; ++i) {
+ fat_arch *afa = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
+ if (afa->cputype == (cpu_type_t)OSSwapHostToBigInt32(textMH->cputype()) && afa->cpusubtype == (cpu_type_t)OSSwapHostToBigInt32(textMH->cpusubtype())) {
+ //fprintf(stderr, "arch already exists in fat dylib\n");
+ return offsetInFatFile;
+ }
+ }
+ }
+ }
+
+ if (dylib_data.empty()) {
+ // Reserve space for the fat header.
+ dylib_data.resize(4096);
+ base_ptr = &dylib_data.front();
+ FH->magic = OSSwapHostToBigInt32(FAT_MAGIC);
+ }
+
FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs);
-
+
FA->cputype = 0; // filled in later
FA->cpusubtype = 0; // filled in later
FA->offset = OSSwapHostToBigInt32(offsetInFatFile);
FA->size = 0; // filled in later
FA->align = OSSwapHostToBigInt32(12);
-
- // Write regular segments into the buffer
- uint64_t totalSize = 0;
- uint64_t textOffsetInCache = 0;
- for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
-
+
+ size_t additionalSize = 0;
+ for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+ if ( strcmp(it->segName, "__LINKEDIT") != 0 )
+ additionalSize += it->sizem;
+ }
+
+ std::vector<uint8_t> new_dylib_data;
+ new_dylib_data.reserve(additionalSize);
+
+ // Write regular segments into the buffer
+ uint64_t textOffsetInCache = 0;
+ for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+
if(strcmp(it->segName, "__TEXT") == 0 ) {
- textOffsetInCache = it->offset;
+ textOffsetInCache = it->offset;
const macho_header<P> *textMH = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+textOffsetInCache);
- FA->cputype = OSSwapHostToBigInt32(textMH->cputype());
+ FA->cputype = OSSwapHostToBigInt32(textMH->cputype());
FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype());
-
- // if this cputype/subtype already exist in fat header, then return immediately
- for(uint32_t i=0; i < nfat_archs-1; ++i) {
- fat_arch *afa = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
-
- if( afa->cputype == FA->cputype
- && afa->cpusubtype == FA->cpusubtype) {
- //fprintf(stderr, "arch already exists in fat dylib\n");
- dylib_data.resize(offsetInFatFile);
- return offsetInFatFile;
- }
- }
- }
-
- //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
- std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data));
- base_ptr = &dylib_data.front();
- totalSize += it->sizem;
- }
-
- FA->size = OSSwapHostToBigInt32(totalSize);
-
- // optimize linkedit
- uint64_t newSize = dylib_data.size();
- optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
-
- // update fat header with new file size
- dylib_data.resize((size_t)(offsetInFatFile+newSize));
- base_ptr = &dylib_data.front();
- FA->size = OSSwapHostToBigInt32(newSize);
+ }
+
+ //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
+ // Copy all but the __LINKEDIT. It will be copied later during the optimizer in to a temporary buffer but it would
+ // not be efficient to copy it all now for each dylib.
+ if (strcmp(it->segName, "__LINKEDIT") == 0 )
+ continue;
+ std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(new_dylib_data));
+ }
+
+ // optimize linkedit
+ std::vector<uint8_t> new_linkedit_data;
+ new_linkedit_data.reserve(1 << 20);
+
+ LinkeditOptimizer<A> linkeditOptimizer;
+ macho_header<P>* mh = (macho_header<P>*)&new_dylib_data.front();
+ linkeditOptimizer.optimize_loadcommands(mh);
+ linkeditOptimizer.optimize_linkedit(new_linkedit_data, textOffsetInCache, mapped_cache);
+
+ new_dylib_data.insert(new_dylib_data.end(), new_linkedit_data.begin(), new_linkedit_data.end());
+
+ // Page align file
+ while (new_dylib_data.size() % 4096)
+ new_dylib_data.push_back(0);
+
+ // update fat header with new file size
+ FA->size = OSSwapHostToBigInt32(new_dylib_data.size());
#undef FH
#undef FA
- return offsetInFatFile;
-}
+ dylib_data.insert(dylib_data.end(), new_dylib_data.begin(), new_dylib_data.end());
+ return offsetInFatFile;
+}
+
+typedef __typeof(dylib_maker<x86>) dylib_maker_func;
+typedef void (^progress_block)(unsigned current, unsigned total);
+
+class SharedCacheExtractor;
+struct SharedCacheDylibExtractor {
+ SharedCacheDylibExtractor(const char* name, std::vector<seg_info> segInfo)
+ : name(name), segInfo(segInfo) { }
+
+ void extractCache(SharedCacheExtractor& context);
+ const char* name;
+ const std::vector<seg_info> segInfo;
+ int result = 0;
+};
+
+struct SharedCacheExtractor {
+ SharedCacheExtractor(const NameToSegments& map,
+ const char* extraction_root_path,
+ dylib_maker_func* dylib_create_func,
+ void* mapped_cache,
+ progress_block progress)
+ : map(map), extraction_root_path(extraction_root_path),
+ dylib_create_func(dylib_create_func), mapped_cache(mapped_cache),
+ progress(progress) {
+
+ extractors.reserve(map.size());
+ for (const std::pair<const char*, std::vector<seg_info>>& it : map)
+ extractors.emplace_back(it.first, it.second);
+
+ // Limit the number of open files. 16 seems to give better performance than higher numbers.
+ sema = dispatch_semaphore_create(16);
+ }
+ int extractCaches();
+
+ static void extractCache(void *ctx, size_t i);
+
+ const NameToSegments& map;
+ std::vector<SharedCacheDylibExtractor> extractors;
+ dispatch_semaphore_t sema;
+ const char* extraction_root_path;
+ dylib_maker_func* dylib_create_func;
+ void* mapped_cache;
+ progress_block progress;
+ std::atomic_int count = { 0 };
+};
+
+int SharedCacheExtractor::extractCaches() {
+ dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
+ dispatch_apply_f(map.size(), process_queue,
+ this, extractCache);
+
+ int result = 0;
+ for (const SharedCacheDylibExtractor& extractor : extractors) {
+ if (extractor.result != 0) {
+ result = extractor.result;
+ break;
+ }
+ }
+ return result;
+}
+
+void SharedCacheExtractor::extractCache(void *ctx, size_t i) {
+ SharedCacheExtractor& context = *(SharedCacheExtractor*)ctx;
+ dispatch_semaphore_wait(context.sema, DISPATCH_TIME_FOREVER);
+ context.extractors[i].extractCache(context);
+ dispatch_semaphore_signal(context.sema);
+}
+
+void SharedCacheDylibExtractor::extractCache(SharedCacheExtractor &context) {
+
+ char dylib_path[PATH_MAX];
+ strcpy(dylib_path, context.extraction_root_path);
+ strcat(dylib_path, "/");
+ strcat(dylib_path, name);
+
+ //printf("%s with %lu segments\n", dylib_path, it->second.size());
+ // make sure all directories in this path exist
+ make_dirs(dylib_path);
+
+ // open file, create if does not already exist
+ int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
+ if ( fd == -1 ) {
+ fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
+ result = -1;
+ return;
+ }
+
+ struct stat statbuf;
+ if (fstat(fd, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
+ close(fd);
+ result = -1;
+ return;
+ }
+
+ std::vector<uint8_t> vec((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);
+ result = -1;
+ return;
+ }
+
+ const size_t offset = context.dylib_create_func(context.mapped_cache, vec, segInfo);
+ context.progress(context.count++, (unsigned)context.map.size());
+
+ if(offset != vec.size()) {
+ //Write out the first page, and everything after offset
+ if( pwrite(fd, &vec.front(), 4096, 0) == -1
+ || pwrite(fd, &vec.front() + offset, vec.size() - offset, offset) == -1) {
+ fprintf(stderr, "error writing, errnor=%d\n", errno);
+ result = -1;
+ }
+ }
+
+ close(fd);
+}
+
+static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) {
+ // First check that the size is good.
+ // Note the shared cache may not have a codeSignatureSize value set so we need to first make
+ // sure we have space for the CS_SuperBlob, then later crack that to check for the size of the rest.
+ const DyldSharedCache* dyldSharedCache = (DyldSharedCache*)mapped_cache;
+ uint64_t requiredSizeForCSSuperBlob = dyldSharedCache->header.codeSignatureOffset + sizeof(CS_SuperBlob);
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)mapped_cache + dyldSharedCache->header.mappingOffset);
+ if ( requiredSizeForCSSuperBlob > size ) {
+ fprintf(stderr, "Error: dyld shared cache size 0x%08llx is less than required size of 0x%08llx.\n", size, requiredSizeForCSSuperBlob);
+ return -1;
+ }
+
+ // Now see if the code signatures are valid as that tells us the pages aren't corrupt.
+ // First find all of the regions of the shared cache we computed cd hashes
+ std::vector<std::pair<uint64_t, uint64_t>> sharedCacheRegions;
+ sharedCacheRegions.emplace_back(std::make_pair(mappings[0].fileOffset, mappings[0].fileOffset + mappings[0].size));
+ sharedCacheRegions.emplace_back(std::make_pair(mappings[1].fileOffset, mappings[1].fileOffset + mappings[1].size));
+ sharedCacheRegions.emplace_back(std::make_pair(mappings[2].fileOffset, mappings[2].fileOffset + mappings[2].size));
+ if (dyldSharedCache->header.localSymbolsSize)
+ sharedCacheRegions.emplace_back(std::make_pair(dyldSharedCache->header.localSymbolsOffset, dyldSharedCache->header.localSymbolsOffset + dyldSharedCache->header.localSymbolsSize));
+ size_t inBbufferSize = 0;
+ for (auto& sharedCacheRegion : sharedCacheRegions)
+ inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
+ uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+
+ // Now take the cd hash from the cache itself and validate the regions we found.
+ uint8_t* codeSignatureRegion = (uint8_t*)mapped_cache + dyldSharedCache->header.codeSignatureOffset;
+ CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(codeSignatureRegion);
+ if (sb->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE)) {
+ fprintf(stderr, "Error: dyld shared cache code signature magic is incorrect.\n");
+ return -1;
+ }
+
+ size_t sbSize = ntohl(sb->length);
+ uint64_t requiredSizeForCS = dyldSharedCache->header.codeSignatureOffset + sbSize;
+ if ( requiredSizeForCS > size ) {
+ fprintf(stderr, "Error: dyld shared cache size 0x%08llx is less than required size of 0x%08llx.\n", size, requiredSizeForCS);
+ return -1;
+ }
+
+ // Find the offset to the code directory.
+ CS_CodeDirectory* cd = nullptr;
+ for (unsigned i =0; i != sb->count; ++i) {
+ if (ntohl(sb->index[i].type) == CSSLOT_CODEDIRECTORY) {
+ cd = (CS_CodeDirectory*)(codeSignatureRegion + ntohl(sb->index[i].offset));
+ break;
+ }
+ }
+
+ if (!cd) {
+ fprintf(stderr, "Error: dyld shared cache code signature directory is missing.\n");
+ return -1;
+ }
+
+ if ( (uint8_t*)cd > (codeSignatureRegion + sbSize) ) {
+ fprintf(stderr, "Error: dyld shared cache code signature directory is out of bounds.\n");
+ return -1;
+ }
+
+ if ( cd->magic != htonl(CSMAGIC_CODEDIRECTORY) ) {
+ fprintf(stderr, "Error: dyld shared cache code signature directory magic is incorrect.\n");
+ return -1;
+ }
+
+ if ( ntohl(cd->nCodeSlots) < slotCountFromRegions ) {
+ fprintf(stderr, "Error: dyld shared cache code signature directory num slots is incorrect.\n");
+ return -1;
+ }
+
+ uint32_t dscDigestFormat = kCCDigestNone;
+ switch (cd->hashType) {
+ case CS_HASHTYPE_SHA1:
+ dscDigestFormat = kCCDigestSHA1;
+ break;
+ case CS_HASHTYPE_SHA256:
+ dscDigestFormat = kCCDigestSHA256;
+ break;
+ default:
+ break;
+ }
+
+ if (dscDigestFormat != kCCDigestNone) {
+ const uint64_t csPageSize = 1 << cd->pageSize;
+ size_t hashOffset = ntohl(cd->hashOffset);
+ uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
+ uint8_t cdHashBuffer[cd->hashSize];
+
+ // Skip local symbols for now as those aren't being codesign correctly right now.
+ size_t inBbufferSize = 0;
+ for (auto& sharedCacheRegion : sharedCacheRegions) {
+ if (sharedCacheRegion.first == dyldSharedCache->header.localSymbolsOffset)
+ continue;
+ inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
+ }
+ uint32_t slotCountToProcess = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+
+ for (unsigned i = 0; i != slotCountToProcess; ++i) {
+ // Skip data pages as those may have been slid by ASLR in the extracted file
+ uint64_t fileOffset = i * csPageSize;
+ if ( (fileOffset >= mappings[1].fileOffset) && (fileOffset < (mappings[1].fileOffset + mappings[1].size)) )
+ continue;
+
+ CCDigest(dscDigestFormat, (uint8_t*)mapped_cache + fileOffset, csPageSize, cdHashBuffer);
+ uint8_t* cacheCdHashBuffer = hashSlot + (i * cd->hashSize);
+ if (memcmp(cdHashBuffer, cacheCdHashBuffer, cd->hashSize) != 0) {
+ fprintf(stderr, "Error: dyld shared cache code signature for page %d is incorrect.\n", i);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
- void (^progress)(unsigned current, unsigned total))
+ progress_block progress)
{
- struct stat statbuf;
- if (stat(shared_cache_file_path, &statbuf)) {
- fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
- return -1;
- }
-
- int cache_fd = open(shared_cache_file_path, O_RDONLY);
- if (cache_fd < 0) {
- fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
- return -1;
- }
-
- 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;
- }
-
+ struct stat statbuf;
+ if (stat(shared_cache_file_path, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
+ return -1;
+ }
+
+ int cache_fd = open(shared_cache_file_path, O_RDONLY);
+ if (cache_fd < 0) {
+ fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
+ return -1;
+ }
+
+ 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;
+ }
+
close(cache_fd);
- // instantiate arch specific dylib maker
- size_t (*dylib_create_func)(const void*, std::vector<uint8_t>&, const std::vector<seg_info>&) = NULL;
- if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 )
- dylib_create_func = dylib_maker<x86>;
- else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 )
- dylib_create_func = dylib_maker<x86_64>;
- else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 )
- dylib_create_func = dylib_maker<x86_64>;
- else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 )
- dylib_create_func = dylib_maker<arm>;
- else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 )
- dylib_create_func = dylib_maker<arm>;
- else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 )
- dylib_create_func = dylib_maker<arm>;
- else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 )
- 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");
+ // instantiate arch specific dylib maker
+ dylib_maker_func* dylib_create_func = nullptr;
+ if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 )
+ dylib_create_func = dylib_maker<x86>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 )
+ dylib_create_func = dylib_maker<x86_64>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 )
+ dylib_create_func = dylib_maker<x86_64>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 )
+ dylib_create_func = dylib_maker<arm64>;
+#if SUPPORT_ARCH_arm64e
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64e") == 0 )
+ dylib_create_func = dylib_maker<arm64>;
+#endif
+#if SUPPORT_ARCH_arm64_32
+ else if ( strcmp((char*)mapped_cache, "dyld_v1arm64_32") == 0 )
+ dylib_create_func = dylib_maker<arm64_32>;
+#endif
+ else {
+ fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
munmap(mapped_cache, (size_t)statbuf.st_size);
- return -1;
- }
+ return -1;
+ }
+
+ // Verify that the cache isn't corrupt.
+ if (int result = sharedCacheIsValid(mapped_cache, (uint64_t)statbuf.st_size)) {
+ munmap(mapped_cache, (size_t)statbuf.st_size);
+ return result;
+ }
- // iterate through all images in cache and build map of dylibs and segments
- __block NameToSegments map;
- __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
+ // iterate through all images in cache and build map of dylibs and segments
+ __block NameToSegments map;
+ int result = 0;
+
+ result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize));
});
if(result != 0) {
- fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
+ fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
munmap(mapped_cache, (size_t)statbuf.st_size);
- return result;
- }
-
- // for each dylib instantiate a dylib file
- dispatch_group_t group = dispatch_group_create();
- dispatch_semaphore_t sema = dispatch_semaphore_create(2);
- dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
- dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0);
-
- __block unsigned count = 0;
-
- for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) {
- dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
- dispatch_group_async(group, process_queue, ^{
-
- char dylib_path[PATH_MAX];
- strcpy(dylib_path, extraction_root_path);
- strcat(dylib_path, "/");
- strcat(dylib_path, it->first);
-
- //printf("%s with %lu segments\n", dylib_path, it->second.size());
- // make sure all directories in this path exist
- make_dirs(dylib_path);
-
- // open file, create if does not already exist
- int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
- if ( fd == -1 ) {
- fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
- result = -1;
- return;
- }
-
- struct stat statbuf;
- if (fstat(fd, &statbuf)) {
- fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
- close(fd);
- result = -1;
- return;
- }
-
- 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);
- result = -1;
- return;
- }
-
- const size_t offset = dylib_create_func(mapped_cache, *vec, it->second);
-
- dispatch_group_async(group, writer_queue, ^{
- progress(count++, (unsigned)map.size());
-
- if(offset != vec->size()) {
- //Write out the first page, and everything after offset
- if( pwrite(fd, &vec->front(), 4096, 0) == -1
- || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) {
- fprintf(stderr, "error writing, errnor=%d\n", errno);
- result = -1;
- }
- }
-
- delete vec;
- close(fd);
- dispatch_semaphore_signal(sema);
- });
- });
- }
-
- dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
- dispatch_release(group);
- dispatch_release(writer_queue);
-
+ return result;
+ }
+
+ // for each dylib instantiate a dylib file
+ SharedCacheExtractor extractor(map, extraction_root_path, dylib_create_func, mapped_cache, progress);
+ result = extractor.extractCaches();
+
munmap(mapped_cache, (size_t)statbuf.st_size);
- return result;
+ return result;
}
int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
{
- return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
- ^(unsigned , unsigned) {} );
+ return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
+ ^(unsigned , unsigned) {} );
}
-#if 0
+#if 0
// test program
#include <stdio.h>
#include <stddef.h>
typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
- void (^progress)(unsigned current, unsigned total));
+ void (^progress)(unsigned current, unsigned total));
int main(int argc, const char* argv[])
{
- if ( argc != 3 ) {
- fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
- return 1;
- }
-
- //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
- void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY);
- if ( handle == NULL ) {
- fprintf(stderr, "dsc_extractor.bundle could not be loaded\n");
- return 1;
- }
-
- extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
- if ( proc == NULL ) {
- fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
- return 1;
- }
-
- int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
- fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
- return 0;
+ if ( argc != 3 ) {
+ fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
+ return 1;
+ }
+
+ //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
+ void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY);
+ if ( handle == NULL ) {
+ fprintf(stderr, "dsc_extractor.bundle could not be loaded\n");
+ return 1;
+ }
+
+ extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
+ if ( proc == NULL ) {
+ fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
+ return 1;
+ }
+
+ int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
+ fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
+ return 0;
}
#endif
-
-
+
+
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
#include "CacheFileAbstraction.hpp"
-
+#include "SupportedArchs.h"
namespace dyld {
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);
+#if SUPPORT_ARCH_arm64_32
+ else if ( strcmp((char*)cache, "dyld_v1arm64_32") == 0 )
+ return dyld::walkImages<arm64_32>(cache, shared_cache_size, callback);
+#endif
+#if SUPPORT_ARCH_arm64e
else if ( strcmp((char*)cache, "dyld_v1 arm64e") == 0 )
return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
+#endif
else
return -1;
}
#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page
+
+// The version 3 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
+//
+// There are two cases:
+//
+// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
+// The page contains no values that need rebasing.
+//
+// 2) otherwise...
+// All rebase locations are in one linked list. The offset of the first
+// rebase location in the page is pageStarts[pageIndex].
+//
+// A pointer is one of :
+// {
+// uint64_t pointerValue : 51;
+// uint64_t offsetToNextPointer : 11;
+// uint64_t isBind : 1 = 0;
+// uint64_t authenticated : 1 = 0;
+// }
+// {
+// uint32_t offsetFromSharedCacheBase;
+// uint16_t diversityData;
+// uint16_t hasAddressDiversity : 1;
+// uint16_t key : 2;
+// uint16_t offsetToNextPointer : 11;
+// uint16_t isBind : 1;
+// uint16_t authenticated : 1 = 1;
+// }
+//
+// The code for processing a linked list (chain) is:
+//
+// uint32_t delta = pageStarts[pageIndex];
+// uint8_t* loc = pageStart;
+// do {
+// loc += delta;
+// uintptr_t rawValue = *((uintptr_t*)loc);
+// delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(uint64_t);
+// if (extraBindData.isAuthenticated) {
+// newValue = ( value & 0xFFFFFFFF ) + results->slide + auth_value_add;
+// newValue = sign_using_the_various_bits(newValue);
+// } else {
+// uint64_t top8Bits = value & 0x0007F80000000000ULL;
+// uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+// uint64_t targetValue = ( top8Bits << 13 ) | bottom43Bits;
+// newValue = targetValue + results->slide;
+// }
+// *((uintptr_t*)loc) = newValue;
+// } while (delta != 0 )
+//
+//
+struct dyld_cache_slide_info3
+{
+ uint32_t version; // currently 3
+ uint32_t page_size; // currently 4096 (may also be 16384)
+ uint32_t page_starts_count;
+ uint64_t auth_value_add;
+ uint16_t page_starts[/* page_starts_count */];
+};
+
+#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
+
+
struct dyld_cache_local_symbols_info
{
uint32_t nlistOffset; // offset into this chunk of nlist entries
#include <mach-o/arch.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
#include <mach/mach.h>
#include <map>
#include <vector>
+#include "DyldSharedCache.h"
+
#include "dsc_iterator.h"
#include "dsc_extractor.h"
#include "dyld_cache_format.h"
#include "MachOFileAbstraction.hpp"
#include "CacheFileAbstraction.hpp"
#include "Trie.hpp"
+#include "SupportedArchs.h"
enum Mode {
modeNone,
modeMap,
modeDependencies,
modeSlideInfo,
+ modeVerboseSlideInfo,
modeAcceleratorInfo,
modeTextInfo,
modeLinkEdit,
modeLocalSymbols,
+ modeStrings,
modeInfo,
modeSize,
modeExtract
};
+// mmap() an shared cache file read/only but laid out like it would be at runtime
+static const DyldSharedCache* mapCacheFile(const char* path, size_t& cacheLength)
+{
+ 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;
+ }
+
+ uint8_t firstPage[4096];
+ if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
+ fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
+ return nullptr;
+ }
+ const dyld_cache_header* header = (dyld_cache_header*)firstPage;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
+
+ size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
+ vm_address_t result;
+ kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
+ if ( r != KERN_SUCCESS ) {
+ fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
+ return nullptr;
+ }
+ for (int i=0; i < 3; ++i) {
+ void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
+ PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+ 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);
+
+ cacheLength = statbuf.st_size;
+
+ return (DyldSharedCache*)result;
+}
+
+
void usage() {
- fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -info | -extract <dylib-dir> [ shared-cache-file ] \n");
+ fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract <dylib-dir> [ shared-cache-file ] \n");
}
#if __x86_64__
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_ARCH_8_32__
+ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64_32";
#elif __arm64e__
return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e";
#elif __arm64__
static void checkMode(Mode mode) {
if ( mode != modeNone ) {
- fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n");
+ fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
usage();
exit(1);
}
options.dependentsOfPath = NULL;
options.extractionDir = NULL;
+ bool printStrings = false;
+ bool printExports = false;
+
for (uint32_t i = 1; i < argc; i++) {
const char* opt = argv[i];
if (opt[0] == '-') {
checkMode(options.mode);
options.mode = modeSlideInfo;
}
+ else if (strcmp(opt, "-verbose_slide_info") == 0) {
+ checkMode(options.mode);
+ options.mode = modeVerboseSlideInfo;
+ }
else if (strcmp(opt, "-accelerator_info") == 0) {
checkMode(options.mode);
options.mode = modeAcceleratorInfo;
checkMode(options.mode);
options.mode = modeTextInfo;
}
- else if (strcmp(opt, "-local_symbols") == 0) {
- checkMode(options.mode);
- options.mode = modeLocalSymbols;
- }
+ else if (strcmp(opt, "-local_symbols") == 0) {
+ checkMode(options.mode);
+ options.mode = modeLocalSymbols;
+ }
+ else if (strcmp(opt, "-strings") == 0) {
+ if (options.mode != modeStrings)
+ checkMode(options.mode);
+ options.mode = modeStrings;
+ printStrings = true;
+ }
+ else if (strcmp(opt, "-exports") == 0) {
+ if (options.mode != modeStrings)
+ checkMode(options.mode);
+ options.mode = modeStrings;
+ printExports = true;
+ }
else if (strcmp(opt, "-map") == 0) {
checkMode(options.mode);
options.mode = modeMap;
exit(1);
}
- if ( options.mode != modeSlideInfo ) {
+ if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
if ( options.printUUIDs && (options.mode != modeList) )
fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
exit(1);
}
}
-
- struct stat statbuf;
- if ( ::stat(sharedCachePath, &statbuf) == -1 ) {
- fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno);
- exit(1);
- }
-
- int cache_fd = ::open(sharedCachePath, O_RDONLY);
- if ( cache_fd < 0 ) {
- fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
- exit(1);
- }
- 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);
- }
+
+ const DyldSharedCache* dyldCache = nullptr;
+ bool dyldCacheIsLive = true;
+ size_t cacheLength = 0;
+ if ( sharedCachePath != nullptr ) {
+ dyldCache = mapCacheFile(sharedCachePath, cacheLength);
+ dyldCacheIsLive = false;
+ }
+ else {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
+ fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
+ return 1;
+#else
+ dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
+#endif
+ }
+
+ options.mappedCache = dyldCache;
- if ( options.mode == modeSlideInfo ) {
+ if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
if ( header->slideInfoOffset() == 0 ) {
fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
exit(1);
}
const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
+ const dyldCacheFileMapping<LittleEndian>* textMapping = &mappings[0];
const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
+ const dyldCacheFileMapping<LittleEndian>* linkEditMapping = &mappings[2];
uint64_t dataStartAddress = dataMapping->address();
uint64_t dataSize = dataMapping->size();
- const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+header->slideInfoOffset());
+ uint64_t slideInfoMappedOffset = (header->slideInfoOffset()-linkEditMapping->file_offset()) + (linkEditMapping->address() - textMapping->address());
+ const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+slideInfoMappedOffset);
printf("slide info version=%d\n", slideInfoHeader->version());
if ( slideInfoHeader->version() == 1 ) {
printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset());
for (int i=0; i < slideInfo->page_starts_count(); ++i) {
const uint16_t start = starts[i];
+ auto rebaseChain = [&](uint8_t* pageContent, uint16_t startOffset)
+ {
+ uintptr_t slideAmount = 0;
+ 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;
+ }
+ printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue);
+ pageOffset += delta;
+ }
+ };
+ const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
printf("page[% 5d]: no rebasing\n", i);
}
do {
uint16_t aStart = extras[j];
printf("start=0x%04X ", aStart & 0x3FFF);
+ if ( options.mode == modeVerboseSlideInfo ) {
+ uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
+ uint16_t pageStartOffset = (aStart & 0x3FFF)*4;
+ rebaseChain(page, pageStartOffset);
+ }
done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
++j;
} while ( !done );
}
else {
printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+ if ( options.mode == modeVerboseSlideInfo ) {
+ uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
+ uint16_t pageStartOffset = start*4;
+ rebaseChain(page, pageStartOffset);
+ }
}
}
}
+ else if ( slideInfoHeader->version() == 3 ) {
+ const dyldCacheSlideInfo3<LittleEndian>* slideInfo = (dyldCacheSlideInfo3<LittleEndian>*)(slideInfoHeader);
+ printf("page_size=%d\n", slideInfo->page_size());
+ printf("page_starts_count=%d\n", slideInfo->page_starts_count());
+ printf("auth_value_add=0x%016llX\n", slideInfo->auth_value_add());
+ const uint8_t* dataSegmentStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
+ for (int i=0; i < slideInfo->page_starts_count(); ++i) {
+ const uint16_t start = slideInfo->page_starts(i);
+ if ( start == 0xFFFF ) {
+ printf("page[% 5d]: no rebasing\n", i);
+ }
+ else {
+ printf("page[% 5d]: start=0x%04X\n", i, start);
+ if ( options.mode == modeVerboseSlideInfo ) {
+ typedef Pointer64<LittleEndian> P;
+ typedef typename P::uint_t pint_t;
+ const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size());
+ pint_t delta = start;
+ const uint8_t* rebaseLocation = pageStart;
+ do {
+ rebaseLocation += delta;
+ pint_t value = (pint_t)P::getP(*(uint64_t*)rebaseLocation);
+ delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(pint_t);
+
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(rebaseLocation - pageStart), targetValue);
+ } while (delta != 0);
+ }
+ }
+ }
+ }
+ else if ( slideInfoHeader->version() == 4 ) {
+ const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
+ printf("page_size=%d\n", slideInfo->page_size);
+ printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
+ printf("value_add=0x%016llX\n", slideInfo->value_add);
+ printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
+ const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
+ const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
+ for (int i=0; i < slideInfo->page_starts_count; ++i) {
+ const uint16_t start = starts[i];
+ auto rebaseChainV4 = [&](uint8_t* pageContent, uint16_t startOffset)
+ {
+ uintptr_t slideAmount = 0;
+ 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;
+ uint32_t rawValue = *((uint32_t*)loc);
+ delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
+ uintptr_t value = (rawValue & valueMask);
+ if ( (value & 0xFFFF8000) == 0 ) {
+ // small positive non-pointer, use as-is
+ }
+ else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
+ // small negative non-pointer
+ value |= 0xC0000000;
+ }
+ else {
+ value += valueAdd;
+ value += slideAmount;
+ }
+ printf(" [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
+ pageOffset += delta;
+ }
+ };
+ const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
+ if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
+ printf("page[% 5d]: no rebasing\n", i);
+ }
+ else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+ printf("page[% 5d]: ", i);
+ int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+ bool done = false;
+ do {
+ uint16_t aStart = extras[j];
+ printf("start=0x%04X ", aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+ if ( options.mode == modeVerboseSlideInfo ) {
+ uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
+ uint16_t pageStartOffset = (aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
+ rebaseChainV4(page, pageStartOffset);
+ }
+ done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
+ ++j;
+ } while ( !done );
+ printf("\n");
+ }
+ else {
+ printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+ if ( options.mode == modeVerboseSlideInfo ) {
+ uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
+ uint16_t pageStartOffset = start*4;
+ rebaseChainV4(page, pageStartOffset);
+ }
+ }
+ }
+ }
+ return 0;
}
- else if ( options.mode == modeInfo ) {
+
+ if ( options.mode == modeInfo ) {
const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
printf("uuid: ");
if ( header->mappingOffset() >= 0x68 ) {
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));
+ uint32_t bitfield = *((uint32_t*)(((char*)header) + 0xDC));
+ uint32_t simulator = bitfield & 0x200;
+ uint32_t locallyBuiltCache = bitfield & 0x400;
switch (platform) {
case 1:
printf("platform: macOS\n");
break;
case 2:
- if ( simulator & 0x400 )
+ if ( simulator )
printf("platform: iOS simulator\n");
else
printf("platform: iOS\n");
break;
case 3:
- if ( simulator & 0x400 )
+ if ( simulator )
printf("platform: tvOS simulator\n");
else
printf("platform: tvOS\n");
break;
case 4:
- if ( simulator & 0x400 )
+ if ( simulator )
printf("platform: watchOS simulator\n");
else
printf("platform: watchOS\n");
default:
printf("platform: 0x%08X 0x%08X\n", platform, simulator);
}
+ printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
}
printf("image count: %u\n", header->imagesCount());
if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
mappings[i].address(), mappings[i].address() + mappings[i].size());
}
if ( header->codeSignatureOffset() != 0 ) {
- uint64_t size = statbuf.st_size - header->codeSignatureOffset();
+ uint64_t size = cacheLength - header->codeSignatureOffset();
uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
if ( size != 0 )
printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
#endif
}
}
+ else if ( options.mode == modeStrings ) {
+ if (printStrings) {
+ dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+ const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+ int64_t slide = ma->getSlide();
+ ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+ if ( malformedSectionRange ) {
+ stop = true;
+ return;
+ }
+ const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
+ const char* s = (char*)content;
+ const char* end = s + info.sectSize;
+ while ( s < end ) {
+ printf("%s: %s\n", ma->installName(), s);
+ while (*s != '\0' )
+ ++s;
+ ++s;
+ }
+ }
+ });
+ });
+ }
+
+ if (printExports) {
+ dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+ const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+ uint32_t exportTrieRuntimeOffset;
+ uint32_t exportTrieSize;
+ if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
+ const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
+ const uint8_t* end = start + exportTrieSize;
+ std::vector<ExportInfoTrie::Entry> exports;
+ if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
+ return;
+ }
+
+ for (const ExportInfoTrie::Entry& entry: exports) {
+ printf("%s: %s\n", ma->installName(), entry.name.c_str());
+ }
+ }
+ });
+ }
+ }
else if ( options.mode == modeExtract ) {
char pathBuffer[PATH_MAX];
uint32_t bufferSize = PATH_MAX;
break;
case modeNone:
case modeInfo:
- case modeSlideInfo:
+ case modeSlideInfo:
+ case modeVerboseSlideInfo:
case modeAcceleratorInfo:
case modeTextInfo:
case modeLocalSymbols:
+ case modeStrings:
case modeExtract:
break;
}
break;
case modeNone:
case modeInfo:
- case modeSlideInfo:
+ case modeSlideInfo:
+ case modeVerboseSlideInfo:
case modeAcceleratorInfo:
case modeTextInfo:
- case modeLocalSymbols:
+ case modeLocalSymbols:
+ case modeStrings:
case modeExtract:
break;
}
}
- else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0)
- || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) {
+ else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0)
+ || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0)
+#if SUPPORT_ARCH_arm64_32
+ || (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0)
+#endif
+ ) {
switch ( options.mode ) {
case modeList:
callback = print_list<arm>;
break;
case modeNone:
case modeInfo:
- case modeSlideInfo:
+ case modeSlideInfo:
+ case modeVerboseSlideInfo:
case modeAcceleratorInfo:
case modeTextInfo:
- case modeLocalSymbols:
+ case modeLocalSymbols:
+ case modeStrings:
case modeExtract:
break;
}
}
else if ( (strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0)
- || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0) ) {
+#if SUPPORT_ARCH_arm64e
+ || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0)
+#endif
+ ) {
switch ( options.mode ) {
case modeList:
callback = print_list<arm64>;
break;
case modeNone:
case modeInfo:
+ case modeSlideInfo:
+ case modeVerboseSlideInfo:
+ case modeAcceleratorInfo:
+ case modeTextInfo:
+ case modeLocalSymbols:
+ case modeStrings:
+ case modeExtract:
+ break;
+ }
+ }
+#if SUPPORT_ARCH_arm64_32
+ else if ( (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0) ) {
+ switch ( options.mode ) {
+ case modeList:
+ callback = print_list<arm64_32>;
+ break;
+ case modeMap:
+ callback = print_map<arm64_32>;
+ break;
+ case modeDependencies:
+ callback = print_dependencies<arm64_32>;
+ break;
+ case modeLinkEdit:
+ callback = process_linkedit<arm64_32>;
+ break;
+ case modeSize:
+ callback = collect_size<arm64_32>;
+ break;
+ case modeNone:
+ case modeInfo:
case modeSlideInfo:
+ case modeVerboseSlideInfo:
case modeAcceleratorInfo:
case modeTextInfo:
- case modeLocalSymbols:
+ case modeLocalSymbols:
+ case modeStrings:
case modeExtract:
break;
}
- }
- else {
+ }
+#endif
+ else {
fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
exit(1);
}
__block Results results;
results.dependentTargetFound = false;
- int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size,
+ int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)cacheLength,
^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) {
(callback)(dylibInfo, segInfo, options, results);
});
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/mount.h>
+#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
+#include "Tracing.h"
+
#include "ImageLoader.h"
// this is called by initializeMainExecutable() to interpose on the initial set of images
void ImageLoader::applyInterposing(const LinkContext& context)
{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
if ( fgInterposingTuples.size() != 0 )
this->recursiveApplyInterposing(context);
}
return address;
}
+void ImageLoader::applyInterposingToDyldCache(const LinkContext& context) {
+#if USES_CHAINED_BINDS
+ if (!context.dyldCache)
+ return;
+ if (fgInterposingTuples.empty())
+ return;
+ // For each of the interposed addresses, see if any of them are in the shared cache. If so, find
+ // that image and apply its patch table to all uses.
+ uintptr_t cacheStart = (uintptr_t)context.dyldCache;
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interpose: Trying to interpose address 0x%08llx\n", (uint64_t)it->replacee);
+ uint32_t imageIndex;
+ uint32_t cacheOffsetOfReplacee = (uint32_t)(it->replacee - cacheStart);
+ if (!context.dyldCache->addressInText(cacheOffsetOfReplacee, &imageIndex))
+ continue;
+ dyld3::closure::ImageNum imageInCache = imageIndex+1;
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interpose: Found shared cache image %d for 0x%08llx\n", imageInCache, (uint64_t)it->replacee);
+ const dyld3::closure::Image* image = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageInCache);
+ image->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* exportName) {
+ // Skip patching anything other than this symbol
+ if (cacheOffsetOfImpl != cacheOffsetOfReplacee)
+ return;
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interpose: Patching uses of symbol %s in shared cache binary at %s\n", exportName, image->path());
+ uintptr_t newLoc = it->replacement;
+ image->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(dyld3::closure::Image::PatchableExport::PatchLocation patchLocation) {
+ uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+#if __has_feature(ptrauth_calls)
+ if ( patchLocation.authenticated ) {
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+ fixupInfo.authRebase.auth = true;
+ fixupInfo.authRebase.addrDiv = patchLocation.usesAddressDiversity;
+ fixupInfo.authRebase.diversity = patchLocation.discriminator;
+ fixupInfo.authRebase.key = patchLocation.key;
+ *loc = fixupInfo.signPointer(loc, newLoc + patchLocation.getAddend());
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interpose: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+ loc, (void*)*loc, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName());
+ return;
+ }
+#endif
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interpose: *%p = 0x%0llX (dyld cache patch) to %s\n", loc, newLoc + patchLocation.getAddend(), exportName);
+ *loc = newLoc + patchLocation.getAddend();
+ });
+ });
+ }
+#endif
+}
+
void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count)
{
for(size_t i=0; i < count; ++i) {
}
}
+// <rdar://problem/29099600> dyld should tell the kernel when it is doing root fix-ups
+void ImageLoader::vmAccountingSetSuspended(const LinkContext& context, bool suspend)
+{
+#if __arm__ || __arm64__
+ static bool sVmAccountingSuspended = false;
+ if ( suspend == sVmAccountingSuspended )
+ return;
+ if ( context.verboseBind )
+ dyld::log("set vm.footprint_suspend=%d\n", suspend);
+ int newValue = suspend ? 1 : 0;
+ int oldValue = 0;
+ size_t newlen = sizeof(newValue);
+ size_t oldlen = sizeof(oldValue);
+ int ret = sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+ if ( context.verboseBind && (ret != 0) )
+ dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno);
+ sVmAccountingSuspended = suspend;
+#endif
+}
+
void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
{
// we only do the loading step for preflights
if ( preflightOnly )
return;
-
+
uint64_t t1 = mach_absolute_time();
context.clearAllDepths();
this->recursiveUpdateDepth(context.imageCount());
- uint64_t t2 = mach_absolute_time();
- this->recursiveRebase(context);
- context.notifyBatch(dyld_image_state_rebased, false);
-
- uint64_t t3 = mach_absolute_time();
- this->recursiveBind(context, forceLazysBound, neverUnload);
-
- uint64_t t4 = mach_absolute_time();
- if ( !context.linkingMainExecutable )
- this->weakBind(context);
- uint64_t t5 = mach_absolute_time();
+ __block uint64_t t2, t3, t4, t5;
+ {
+ dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
+ t2 = mach_absolute_time();
+ this->recursiveRebase(context);
+ context.notifyBatch(dyld_image_state_rebased, false);
+
+ t3 = mach_absolute_time();
+ if ( !context.linkingMainExecutable )
+ this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
+
+ t4 = mach_absolute_time();
+ if ( !context.linkingMainExecutable )
+ this->weakBind(context);
+ t5 = mach_absolute_time();
+ }
- context.notifyBatch(dyld_image_state_bound, false);
+ if ( !context.linkingMainExecutable )
+ context.notifyBatch(dyld_image_state_bound, false);
uint64_t t6 = mach_absolute_time();
std::vector<DOFInfo> dofs;
// interpose any dynamically loaded images
if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
this->recursiveApplyInterposing(context);
}
-
+
// clear error strings
(*context.setErrorStrings)(0, NULL, NULL, NULL);
}
try {
unsigned cacheIndex;
- dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex);
+ bool enforceIOSMac = false;
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)this->machHeader();
+ if ( mf->supportsPlatform(dyld3::Platform::iOSMac) && !mf->supportsPlatform(dyld3::Platform::macOS) )
+ enforceIOSMac = true;
+ #endif
+ dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, enforceIOSMac, cacheIndex);
if ( dependentLib == this ) {
// found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168
- dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex);
+ dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, enforceIOSMac, cacheIndex);
if ( dependentLib != this )
dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath());
}
}
// check found library version is compatible
// <rdar://problem/89200806> 0xFFFFFFFF is wildcard that matches any version
- if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion) ) {
+ if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion)
+ && ((dyld3::MachOFile*)(dependentLib->machHeader()))->enforceCompatVersion() ) {
// record values for possible use by CrashReporter or Finder
dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff,
}
}
-
+void ImageLoader::recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload)
+{
+ this->recursiveBind(context, forceLazysBound, neverUnload);
+ vmAccountingSetSuspended(context, false);
+}
void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload)
{
}
}
+
+// These are mangled symbols for all the variants of operator new and delete
+// which a main executable can define (non-weak) and override the
+// weak-def implementation in the OS.
+static const char* sTreatAsWeak[] = {
+ "__Znwm", "__ZnwmRKSt9nothrow_t",
+ "__Znam", "__ZnamRKSt9nothrow_t",
+ "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
+ "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
+ "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
+ "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
+ "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
+ "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
+};
+
+
+
void ImageLoader::weakBind(const LinkContext& context)
{
if ( context.verboseWeakBind )
}
}
}
-
+
}
}
-
+
+#if __arm64e__
+ for (int i=0; i < count; ++i) {
+ if ( imagesNeedingCoalescing[i]->usesChainedFixups() ) {
+ // during binding of references to weak-def symbols, the dyld cache was patched
+ // but if main executable has non-weak override of operator new or delete it needs is handled here
+ if ( !imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) {
+ for (const char* weakSymbolName : sTreatAsWeak) {
+ const ImageLoader* dummy;
+ imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy);
+ }
+ }
+ }
+ else {
+ // look for weak def symbols in this image which may override the cache
+ ImageLoader::CoalIterator coaler;
+ imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0);
+ imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+ while ( !coaler.done ) {
+ imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+ const ImageLoader* dummy;
+ // a side effect of resolveWeak() is to patch cache
+ imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy);
+ }
+ }
+ }
+#endif
+
// mark all as having all weak symbols bound
for(int i=0; i < count; ++i) {
imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]);
}
}
+
uint64_t t2 = mach_absolute_time();
fgTotalWeakBindTime += t2 - t1;
static uint64_t sUnitsPerSecond = 0;
if ( sUnitsPerSecond == 0 ) {
struct mach_timebase_info timeBaseInfo;
- if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) {
- sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
- }
+ if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS )
+ return;
+ sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
}
if ( partTime < sUnitsPerSecond ) {
uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond);
} while (byte & 0x80);
// sign extend negative numbers
if ( (byte & 0x40) != 0 )
- result |= (-1LL) << bit;
+ result |= (~0ULL) << bit;
return (intptr_t)result;
}
#include <TargetConditionals.h>
#include <vector>
#include <new>
+#include <uuid/uuid.h>
#if __arm__
#include <mach/vm_page_size.h>
#include "mach-o/dyld_images.h"
#include "mach-o/dyld_priv.h"
+#include "DyldSharedCache.h"
#if __i386__
#define SHARED_REGION_BASE SHARED_REGION_BASE_I386
#define SUPPORT_CLASSIC_MACHO __arm__
#define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__)
#define INITIAL_IMAGE_COUNT 150
- #define SUPPORT_ACCELERATE_TABLES (__arm__ || __arm64__)
+ #define SUPPORT_ACCELERATE_TABLES !TARGET_IPHONE_SIMULATOR
#define SUPPORT_ROOT_PATH TARGET_IPHONE_SIMULATOR
+ #define USES_CHAINED_BINDS (__arm64e__)
#else
#define SPLIT_SEG_SHARED_REGION_SUPPORT 0
#define SPLIT_SEG_DYLIB_SUPPORT __i386__
#define INITIAL_IMAGE_COUNT 200
#define SUPPORT_ACCELERATE_TABLES 0
#define SUPPORT_ROOT_PATH 1
+ #define USES_CHAINED_BINDS 0
#endif
#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024)
#define MH_HAS_OBJC 0x40000000
+
// <rdar://problem/13590567> optimize away dyld's initializers
#define VECTOR_NEVER_DESTRUCTED(type) \
namespace std { \
void addTime(const char* name, uint64_t time);
};
+
+ typedef void (^CoalesceNotifier)(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh);
struct LinkContext {
- ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex);
+ ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex);
void (*terminationRecorder)(ImageLoader* image);
bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
- bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
+ bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image, CoalesceNotifier);
unsigned int (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]);
void (*undefinedHandler)(const char* name);
MappedRegion* (*getAllMappedRegions)(MappedRegion*);
const char* progname;
ProgramVars programVars;
ImageLoader* mainExecutable;
- const char* imageSuffix;
+ const char* const * imageSuffix;
#if SUPPORT_ROOT_PATH
const char** rootPaths;
#endif
+ const DyldSharedCache* dyldCache;
const dyld_interpose_tuple* dynamicInterposeArray;
size_t dynamicInterposeCount;
PrebindMode prebindUsage;
SharedRegionMode sharedRegionMode;
bool dyldLoadedAtSameAddressNeededBySharedCache;
bool strictMachORequired;
- bool requireCodeSignature;
+ bool allowAtPaths;
+ bool allowEnvVarsPrint;
+ bool allowEnvVarsPath;
+ bool allowEnvVarsSharedCache;
+ bool allowClassicFallbackPaths;
+ bool allowInsertFailures;
bool mainExecutableCodeSigned;
bool preFetchDisabled;
bool prebinding;
bool linkingMainExecutable;
bool startedInitializingMainExecutable;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- bool processIsRestricted;
- bool processUsingLibraryValidation;
+ bool marzipan;
#endif
bool verboseOpts;
bool verboseEnv;
bool leaveMapped() { return fLeaveMapped; }
// image resides in dyld shared cache
- virtual bool inSharedCache() const = 0;
+ virtual bool inSharedCache() const { return false; };
// checks if the specifed address is within one of this image's segments
virtual bool containsAddress(const void* addr) const;
// st_mtime from stat() on file
time_t lastModified() const;
- // only valid for main executables, returns a pointer its entry point from LC_UNIXTHREAD
- virtual void* getThreadPC() const = 0;
+ // only valid for main executables, returns a pointer its entry point from LC_MAIN
+ virtual void* getEntryFromLC_MAIN() const = 0;
- // only valid for main executables, returns a pointer its main from LC_<MAIN
- virtual void* getMain() const = 0;
+ // only valid for main executables, returns a pointer its main from LC_UNIXTHREAD
+ virtual void* getEntryFromLC_UNIXTHREAD() const = 0;
// dyld API's require each image to have an associated mach_header
virtual const struct mach_header* machHeader() const = 0;
// fills in info about __eh_frame and __unwind_info sections
virtual void getUnwindInfo(dyld_unwind_sections* info) = 0;
+ // given a pointer into an image, find which segment and section it is in
+ virtual const struct macho_section* findSection(const void* imageInterior) const = 0;
+
// given a pointer into an image, find which segment and section it is in
virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0;
// Image has objc sections, so information objc about when it comes and goes
virtual bool notifyObjC() const { return false; }
+ virtual bool overridesCachedDylib(uint32_t& num) const { return false; }
+ virtual void setOverridesCachedDylib(uint32_t num) { }
+
+
//
// A segment is a chunk of an executable file that is mapped into memory.
//
virtual uint32_t minOSVersion() const = 0;
// if the image contains interposing functions, register them
- virtual void registerInterposing() = 0;
+ virtual void registerInterposing(const LinkContext& context) = 0;
+
+ virtual bool usesChainedFixups() const { return false; }
+
// when resolving symbols look in subImage if symbol can't be found
void reExport(ImageLoader* subImage);
-
+
+ virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
+ virtual void recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload);
void weakBind(const LinkContext& context);
void applyInterposing(const LinkContext& context);
bool isReferencedDownward() { return fIsReferencedDownward; }
-
+ virtual uintptr_t resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
+ const ImageLoader** foundIn) { return 0; }
+
// triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo);
static bool haveInterposingTuples() { return !fgInterposingTuples.empty(); }
static void clearInterposingTuples() { fgInterposingTuples.clear(); }
+ static void applyInterposingToDyldCache(const LinkContext& context);
+
bool dependsOn(ImageLoader* image);
void setPath(const char* path);
static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end);
static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end);
+ void vmAccountingSetSuspended(const LinkContext& context, bool suspend);
+
protected:
// abstract base class so all constructors protected
ImageLoader(const char* path, unsigned int libCount);
virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
virtual unsigned recursiveUpdateDepth(unsigned int maxDepth);
virtual void recursiveRebase(const LinkContext& context);
- virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
virtual void recursiveApplyInterposing(const LinkContext& context);
virtual void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
static uint64_t fgTotalObjCSetupTime;
static uint64_t fgTotalDebuggerPausedTime;
static uint64_t fgTotalRebindCacheTime;
-protected:
static uint64_t fgTotalRebaseTime;
static uint64_t fgTotalBindTime;
static uint64_t fgTotalWeakBindTime;
static uint64_t fgTotalDOF;
static uint64_t fgTotalInitTime;
+
+protected:
static std::vector<InterposeTuple> fgInterposingTuples;
const char* fPath;
#include <stdint.h>
#include <System/sys/codesign.h>
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
#include "ImageLoaderMachO.h"
#include "ImageLoaderMachOCompressed.h"
#if SUPPORT_CLASSIC_MACHO
#define TOOL_LD 3
#endif
-
-
-#if TARGET_IPHONE_SIMULATOR
- #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
-#else
- #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib"
-#endif
+#define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib"
// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
#if __LP64__
fReadOnlyImportSegment(false),
#endif
fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false),
- fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false)
+ fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false), fOverrideOfCacheImageNum(0)
{
fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0);
case LC_VERSION_MIN_WATCHOS:
case LC_VERSION_MIN_TVOS:
case LC_VERSION_MIN_IPHONEOS:
- throw "mach-o, but built for simulator (not macOS)";
+ if ( !context.marzipan )
+ throw "mach-o, but built for simulator (not macOS)";
break;
#endif
}
for(unsigned int i=0; i < fSegmentsCount; ++i) {
// set up pointer to __LINKEDIT segment
if ( strcmp(segName(i),"__LINKEDIT") == 0 ) {
- if ( context.requireCodeSignature && (segFileOffset(i) > fCoveredCodeLength))
+ #if !__MAC_OS_X_VERSION_MIN_REQUIRED
+ // <rdar://problem/42419336> historically, macOS never did this check
+ if ( segFileOffset(i) > fCoveredCodeLength )
dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName());
+ #endif
fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i));
}
#if TEXT_RELOC_SUPPORT
void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context)
{
+ dyld3::ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 0);
// if dylib being loaded has no code signature load command
if ( codeSigCmd == NULL) {
- if (context.requireCodeSignature ) {
- // if we require dylibs to be codesigned there needs to be a signature.
- dyld::throwf("required code signature missing for '%s'\n", this->getPath());
- } else {
- disableCoverageCheck();
- }
+ disableCoverageCheck();
}
else {
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// <rdar://problem/13622786> ignore code signatures in binaries built with pre-10.9 tools
if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) {
+ disableCoverageCheck();
return;
}
#endif
+
fsignatures_t siginfo;
- siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file
+ siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file
siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file
siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD
int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
if ( result == -1 ) {
if ( (errno == EPERM) || (errno == EBADEXEC) )
dyld::throwf("code signature invalid for '%s'\n", this->getPath());
- if ( context.verboseCodeSignatures )
+ if ( context.verboseCodeSignatures )
dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno);
siginfo.fs_file_start = UINT64_MAX;
} else if ( context.verboseCodeSignatures ) {
dyld::log("dyld: Registered code signature for %s\n", this->getPath());
}
fCoveredCodeLength = siginfo.fs_file_start;
+ }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( context.processUsingLibraryValidation ) {
- fchecklv checkInfo;
- char messageBuffer[512];
- messageBuffer[0] = '\0';
- checkInfo.lv_file_start = offsetInFatFile;
- checkInfo.lv_error_message_size = sizeof(messageBuffer);
- checkInfo.lv_error_message = messageBuffer;
- int res = fcntl(fd, F_CHECK_LV, &checkInfo);
- if ( res == -1 ) {
- dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer);
- }
+ {
+ fchecklv checkInfo;
+ char messageBuffer[512];
+ messageBuffer[0] = '\0';
+ checkInfo.lv_file_start = offsetInFatFile;
+ checkInfo.lv_error_message_size = sizeof(messageBuffer);
+ checkInfo.lv_error_message = messageBuffer;
+ int res = fcntl(fd, F_CHECK_LV, &checkInfo);
+ if ( res == -1 ) {
+ dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer);
}
-#endif
}
}
}
#endif
if (codeSigCmd != NULL) {
- void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat);
+ void *fdata = xmmap(NULL, lenFileData, PROT_READ, MAP_SHARED, fd, offsetInFat);
if ( fdata == MAP_FAILED ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( context.processUsingLibraryValidation ) {
- dyld::throwf("cannot load image with wrong team ID in process using Library Validation");
- }
- else
-#endif
- {
- int errnoCopy = errno;
- if ( errnoCopy == EPERM ) {
- if ( dyld::sandboxBlockedMmap(getPath()) )
- dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath());
- else
- dyld::throwf("code signing blocked mmap() of '%s'", getPath());
- }
+ int errnoCopy = errno;
+ if ( errnoCopy == EPERM ) {
+ if ( dyld::sandboxBlockedMmap(getPath()) )
+ dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath());
else
- dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath());
+ dyld::throwf("code signing blocked mmap() of '%s'", getPath());
}
+ else
+ dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath());
}
if ( memcmp(fdata, fileData, lenFileData) != 0 )
dyld::throwf("mmap() page compare failed for '%s'", getPath());
return NULL;
}
-void ImageLoaderMachO::registerInterposing()
+void ImageLoaderMachO::registerInterposing(const LinkContext& context)
{
// mach-o files advertise interposing by having a __DATA __interpose section
struct InterposeData { uintptr_t replacement; uintptr_t replacee; };
}
-void* ImageLoaderMachO::getThreadPC() const
+void* ImageLoaderMachO::getEntryFromLC_MAIN() const
{
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
}
-void* ImageLoaderMachO::getMain() const
+void* ImageLoaderMachO::getEntryFromLC_UNIXTHREAD() const
{
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd) {
- case LC_UNIXTHREAD:
- {
- #if __i386__
- const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
- void* entry = (void*)(registers->eip + fSlide);
- #elif __x86_64__
- const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
- void* entry = (void*)(registers->rip + fSlide);
- #elif __arm__
- const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16);
- void* entry = (void*)(registers->__pc + fSlide);
- #elif __arm64__
- const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16);
- void* entry = (void*)(registers->__pc + fSlide);
- #else
- #warning need processor specific code
- #endif
- // <rdar://problem/8543820&9228031> verify entry point is in image
- if ( this->containsAddress(entry) ) {
- return entry;
- }
- }
- break;
+ if ( cmd->cmd == LC_UNIXTHREAD ) {
+ #if __i386__
+ const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
+ void* entry = (void*)(registers->eip + fSlide);
+ // <rdar://problem/8543820&9228031> verify entry point is in image
+ if ( this->containsAddress(entry) )
+ return entry;
+ #elif __x86_64__
+ const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
+ void* entry = (void*)(registers->rip + fSlide);
+ // <rdar://problem/8543820&9228031> verify entry point is in image
+ if ( this->containsAddress(entry) )
+ return entry;
+ #elif __arm64__ && !__arm64e__
+ // temp support until <rdar://39514191> is fixed
+ const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+ void* entry = (void*)(regs64[32] + fSlide); // arm_thread_state64_t.__pc
+ // <rdar://problem/8543820&9228031> verify entry point is in image
+ if ( this->containsAddress(entry) )
+ return entry;
+ #endif
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
const char* pathToAdd = NULL;
const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( context.processIsRestricted && (context.mainExecutable == this) ) {
+ if ( !context.allowAtPaths && (context.mainExecutable == this) ) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath());
break;
}
-#endif
char resolvedPath[PATH_MAX];
if ( realpath(this->getPath(), resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
}
}
else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( context.processIsRestricted ) {
+ if ( !context.allowAtPaths) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath());
break;
}
-#endif
char resolvedPath[PATH_MAX];
if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) {
char newRealPath[strlen(resolvedPath) + strlen(path)];
}
}
}
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- else if ( (path[0] != '/') && context.processIsRestricted ) {
+ else if ( (path[0] != '/') && !context.allowAtPaths) {
dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath());
break;
}
-#endif
#if SUPPORT_ROOT_PATH
else if ( (path[0] == '/') && (context.rootPaths != NULL) ) {
// <rdar://problem/5869973> DYLD_ROOT_PATH should apply to LC_RPATH rpaths
}
-bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset)
+const macho_section* ImageLoaderMachO::findSection(const void* imageInterior) const
{
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) {
- if ( segmentName != NULL )
- *segmentName = sect->segname;
- if ( sectionName != NULL )
- *sectionName = sect->sectname;
- if ( sectionOffset != NULL )
- *sectionOffset = unslidInteriorAddress - sect->addr;
- return true;
+ return sect;
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
+ return nullptr;
+}
+
+
+bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset)
+{
+ if (const struct macho_section* sect = findSection(imageInterior)) {
+ const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide();
+ if ( segmentName != NULL )
+ *segmentName = sect->segname;
+ if ( sectionName != NULL )
+ *sectionName = sect->sectname;
+ if ( sectionOffset != NULL )
+ *sectionOffset = unslidInteriorAddress - sect->addr;
+ return true;
+ }
return false;
}
return (const void*)lastAddress;
}
+uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t baseVMAddress,
+ uintptr_t location, uintptr_t value,
+ uint8_t type, const char* symbolName,
+ intptr_t addend, const char* inPath, const char* toPath, const char* msg,
+ ExtraBindData *extraBindData, uintptr_t slide)
+{
+ auto logBind = [&]() {
+ if ( !context.verboseBind )
+ return;
+ if ( addend != 0 ) {
+ dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n",
+ msg, shortName(inPath), (uintptr_t)location,
+ ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
+ symbolName, (uintptr_t)location, value, addend);
+ } else {
+ dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n",
+ msg, shortName(inPath), (uintptr_t)location,
+ ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
+ symbolName, (uintptr_t)location, value);
+ }
+ };
+
-uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value,
- uint8_t type, const char* symbolName,
- intptr_t addend, const char* inPath, const char* toPath, const char* msg)
-{
- // log
- if ( context.verboseBind ) {
- if ( addend != 0 )
- dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n",
- msg, shortName(inPath), (uintptr_t)location,
- ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
- symbolName, (uintptr_t)location, value, addend);
- else
- dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n",
- msg, shortName(inPath), (uintptr_t)location,
- ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
- symbolName, (uintptr_t)location, value);
- }
#if LOG_BINDINGS
// dyld::logBindings("%s: %s\n", targetImage->getShortName(), symbolName);
#endif
uint32_t value32;
switch (type) {
case BIND_TYPE_POINTER:
+ logBind();
// test first so we don't needless dirty pages
if ( *locationToFix != newValue )
*locationToFix = newValue;
break;
- case BIND_TYPE_TEXT_ABSOLUTE32:
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ logBind();
loc32 = (uint32_t*)locationToFix;
value32 = (uint32_t)newValue;
if ( *loc32 != value32 )
*loc32 = value32;
break;
- case BIND_TYPE_TEXT_PCREL32:
+ case BIND_TYPE_TEXT_PCREL32:
+ logBind();
loc32 = (uint32_t*)locationToFix;
value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4));
if ( *loc32 != value32 )
*loc32 = value32;
+ break;
+ case BIND_TYPE_THREADED_BIND:
+ logBind();
+ // test first so we don't needless dirty pages
+ if ( *locationToFix != newValue )
+ *locationToFix = newValue;
+ break;
+ case BIND_TYPE_THREADED_REBASE: {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = *locationToFix & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = *locationToFix & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ newValue = (uintptr_t)(targetValue + slide);
+ if ( context.verboseRebase ) {
+ dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX = 0x%08lX\n", shortName(inPath), (uintptr_t)locationToFix, slide, newValue);
+ }
+ *locationToFix = newValue;
break;
+ }
default:
dyld::throwf("bad bind type %d", type);
}
dd->dyldLazyBinder = (void*)&stub_binding_helper;
}
#endif // !__arm64__
+ // <rdar://problem/40352925> Add work around for existing apps that have deprecated __dyld section
+ const char* installNm = this->getInstallPath();
+ if ( (mh->filetype != MH_DYLIB) || (installNm == NULL) || (strcmp(installNm, "/usr/lib/system/libdyld.dylib") != 0) ) {
+ #if TARGET_OS_OSX
+ // don't allow macOS apps build with 10.14 or later SDK and targeting 10.8 or later to have a __dyld section
+ if ( (minOSVersion() >= 0x000a0800) && (sdkVersion() >= 0x000a0e00) )
+ dyld::throwf("__dyld section not supported in %s", this->getPath());
+ #endif
+ #if TARGET_OS_IOS || TARGET_OS_TV
+ // don't allow iOS apps build with 12.0 or later SDK to have a __dyld section
+ if ( sdkVersion() >= 0x000c0000 )
+ dyld::throwf("__dyld section not supported in %s", this->getPath());
+ #endif
+ #if TARGET_OS_WATCH
+ if ( sdkVersion() >= 0x00050000 )
+ dyld::throwf("__dyld section not supported in %s", this->getPath());
+ #endif
+ }
if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) {
if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup )
dd->dyldFuncLookup = (void*)&_dyld_func_lookup;
// match what crt1.o supplies, then the program has a custom entry point.
// This means it might be doing something that needs to be executed before
// initializers are run.
- if ( memcmp(this->getMain(), sStandardEntryPointInstructions, 16) != 0 ) {
+ if ( memcmp(this->getEntryFromLC_UNIXTHREAD(), sStandardEntryPointInstructions, 16) != 0 ) {
if ( context.verboseInit )
dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n");
context.setRunInitialzersOldWay();
// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
if ( ((this->isPrebindable() && (this->getSlide() == 0)) || fInSharedCache)
&& this->usesTwoLevelNameSpace()
- && this->allDependentLibrariesAsWhenPreBound() ) {
+#if !USES_CHAINED_BINDS
+ && this->allDependentLibrariesAsWhenPreBound()
+#endif
+ ) {
// allow environment variables to disable prebinding
if ( context.bindFlat )
return false;
return false;
}
+static void *stripPointer(void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
void ImageLoaderMachO::doImageInit(const LinkContext& context)
{
switch (cmd->cmd) {
case LC_ROUTINES_COMMAND:
Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
+#if __has_feature(ptrauth_calls)
+ func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
// <rdar://problem/8543820&9228031> verify initializers are in image
- if ( ! this->containsAddress((void*)func) ) {
+ if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
}
if ( context.verboseInit )
dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
- 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);
- });
+ {
+ dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (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);
for (size_t j=0; j < count; ++j) {
Initializer func = inits[j];
// <rdar://problem/8543820&9228031> verify initializers are in image
- if ( ! this->containsAddress((void*)func) ) {
+ if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
- 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);
- });
+ {
+ dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (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
const size_t count = sect->size / sizeof(uintptr_t);
for (size_t j=count; j > 0; --j) {
Terminator func = terms[j-1];
+#if __has_feature(ptrauth_calls)
+ func = (Terminator)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
// <rdar://problem/8543820&9228031> verify terminators are in image
- if ( ! this->containsAddress((void*)func) ) {
+ if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath());
}
if ( context.verboseInit )
uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
int protection = 0;
if ( !segUnaccessible(i) ) {
- // If has text-relocs, don't set x-bit initially.
- // Instead set it later after text-relocs have been done.
- if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) )
+ if ( segExecutable(i) )
protection |= PROT_EXEC;
if ( segReadable(i) )
protection |= PROT_READ;
}
}
+#if TEXT_RELOC_SUPPORT
void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context)
{
vm_address_t addr = segActualLoadAddress(segIndex);
vm_size_t size = segSize(segIndex);
const bool setCurrentPermissions = false;
- vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ;
+ vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ | VM_PROT_COPY;
if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) )
protection |= VM_PROT_EXECUTE;
kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection);
(protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' );
}
}
-
+#endif
const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr)
{
+uintptr_t ImageLoaderMachO::imageBaseAddress() const {
+ //printf("imageBaseAddress: %s %d->%d\n", getPath(), 0, segmentCount());
+ for (unsigned int i = 0, e = segmentCount(); i != e; ++i) {
+ if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) )
+ return segPreferredLoadAddress(i);
+ }
+ return 0;
+}
+
#include <stdint.h>
#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
+#include <mach-o/nlist.h>
+#include <uuid/uuid.h>
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
#include "ImageLoader.h"
#include "mach-o/dyld_images.h"
+#define BIND_TYPE_THREADED_BIND 100
+
+
+#define BIND_TYPE_THREADED_REBASE 102
+
//
// ImageLoaderMachO is a subclass of ImageLoader which loads mach-o format files.
void disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; }
const char* getInstallPath() const;
- virtual void* getMain() const;
- virtual void* getThreadPC() const;
+ virtual void* getEntryFromLC_UNIXTHREAD() const;
+ virtual void* getEntryFromLC_MAIN() const;
virtual const struct mach_header* machHeader() const;
virtual uintptr_t getSlide() const;
virtual const void* getEnd() const;
virtual bool needsInitialization();
virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length);
virtual void getUnwindInfo(dyld_unwind_sections* info);
+ virtual const struct macho_section* findSection(const void* imageInterior) const;
virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset);
virtual bool usablePrebinding(const LinkContext& context) const;
virtual unsigned int segmentCount() const;
virtual uintptr_t segActualLoadAddress(unsigned int) const;
virtual uintptr_t segPreferredLoadAddress(unsigned int) const;
virtual uintptr_t segActualEndAddress(unsigned int) const;
- virtual void registerInterposing();
+ virtual void registerInterposing(const LinkContext& context);
virtual uint32_t sdkVersion() const;
virtual uint32_t minOSVersion() const;
virtual const char* libPath(unsigned int) const;
virtual bool notifyObjC() const { return fNotifyObjC; }
+ virtual bool overridesCachedDylib(uint32_t& num) const { num = fOverrideOfCacheImageNum; return (num != 0); }
+ virtual void setOverridesCachedDylib(uint32_t num) { fOverrideOfCacheImageNum = num; }
+
static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList&);
static uint32_t minOSVersion(const mach_header*);
static bool getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind);
static uintptr_t segPreferredAddress(const mach_header* mh, unsigned segIndex);
- static uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value,
- uint8_t type, const char* symbolName,
- intptr_t addend, const char* inPath, const char* toPath, const char* msg);
+
+ uintptr_t imageBaseAddress() const;
+
+ struct ExtraBindData {
+ ExtraBindData() = default;
+ explicit ExtraBindData(uint64_t d) : data(d) { }
+
+ union {
+ uint64_t data = 0;
+ };
+ bool operator==(const ExtraBindData& other) const {
+ return this->data == other.data;
+ }
+ bool operator!=(const ExtraBindData& other) const {
+ return !(*this == other);
+ }
+ bool operator<(const ExtraBindData& other) const {
+ return data < other.data;
+ }
+
+ };
+
+ static uintptr_t bindLocation(const LinkContext& context, uintptr_t baseVMAddress,
+ uintptr_t location, uintptr_t value,
+ uint8_t type, const char* symbolName,
+ intptr_t addend, const char* inPath, const char* toPath, const char* msg,
+ ExtraBindData *extraBindData,
+ uintptr_t fSlide);
virtual void rebase(const LinkContext& context, uintptr_t slide) = 0;
fHasTerminators : 1,
fNotifyObjC : 1,
fRetainForObjC : 1,
- fRegisteredAsRequiresCoalescing : 1; // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
+ fRegisteredAsRequiresCoalescing : 1, // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
+ fOverrideOfCacheImageNum : 12;
static uint32_t fgSymbolTableBinarySearchs;
return true;
if ( context.imageSuffix != NULL ) {
// when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
- char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1];
- strcpy(reexportAndSuffix, exportThruName);
- strcat(reexportAndSuffix, context.imageSuffix);
- if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 )
- return true;
+ for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+ char reexportAndSuffix[strlen(*suffix)+strlen(exportThruName)+1];
+ strcpy(reexportAndSuffix, exportThruName);
+ strcat(reexportAndSuffix, *suffix);
+ if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 )
+ return true;
+ }
}
}
}
return true;
if ( context.imageSuffix != NULL ) {
// when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end
- char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1];
- strcpy(aSubLibNameAndSuffix, aSubLibName);
- strcat(aSubLibNameAndSuffix, context.imageSuffix);
- if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 )
- return true;
+ for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+ char aSubLibNameAndSuffix[strlen(*suffix)+strlen(aSubLibName)+1];
+ strcpy(aSubLibNameAndSuffix, aSubLibName);
+ strcat(aSubLibNameAndSuffix, *suffix);
+ if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 )
+ return true;
+ }
}
}
break;
return true;
if ( context.imageSuffix != NULL ) {
// when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
- char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1];
- strcpy(umbrellaAndSuffix, aSubUmbrellaName);
- strcat(umbrellaAndSuffix, context.imageSuffix);
- if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 )
- return true;
+ for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+ char umbrellaAndSuffix[strlen(*suffix)+strlen(aSubUmbrellaName)+1];
+ strcpy(umbrellaAndSuffix, aSubUmbrellaName);
+ strcat(umbrellaAndSuffix, *suffix);
+ if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 )
+ return true;
+ }
}
}
break;
// symbol requires searching images with coalesced symbols (not done during prebinding)
if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) {
const Symbol* sym;
- if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) {
+ if ( context.coalescedExportFinder(symbolName, &sym, foundIn, nullptr) ) {
if ( *foundIn != this )
context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn));
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
if ( reloc->r_pcrel )
type = BIND_TYPE_TEXT_PCREL32;
#endif
- this->bindLocation(context, (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+ this->bindLocation(context, this->imageBaseAddress(), (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
boundSomething = true;
}
}
#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>
#include "ImageLoaderMachOCompressed.h"
#include "mach-o/dyld_images.h"
+#include "Closure.h"
+#include "Array.h"
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#endif
+
+#ifndef BIND_OPCODE_THREADED
+#define BIND_OPCODE_THREADED 0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+#define BIND_SUBOPCODE_THREADED_APPLY 0x01
+#endif
+
+
+#ifndef BIND_SPECIAL_DYLIB_WEAK_LOOKUP
+#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP -3
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
+
// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
#if __LP64__
#define RELOC_SIZE 3
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
fgTotalRebaseFixups += count;
break;
default:
- dyld::throwf("bad rebase opcode %d", *p);
+ dyld::throwf("bad rebase opcode %d", *(p-1));
}
}
}
}
+#if USES_CHAINED_BINDS
+static void patchCacheUsesOf(const ImageLoader::LinkContext& context, const dyld3::closure::Image* overriddenImage,
+ uint32_t cacheOffsetOfImpl, const char* symbolName, uintptr_t newImpl)
+{
+ uintptr_t cacheStart = (uintptr_t)context.dyldCache;
+ overriddenImage->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(dyld3::closure::Image::PatchableExport::PatchLocation patchLocation) {
+ uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+#if __has_feature(ptrauth_calls)
+ if ( patchLocation.authenticated ) {
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+ fixupInfo.authRebase.auth = true;
+ fixupInfo.authRebase.addrDiv = patchLocation.usesAddressDiversity;
+ fixupInfo.authRebase.diversity = patchLocation.discriminator;
+ fixupInfo.authRebase.key = patchLocation.key;
+ uintptr_t newValue = fixupInfo.signPointer(loc, newImpl + patchLocation.getAddend());
+ if ( *loc != newValue ) {
+ *loc = newValue;
+ if ( context.verboseBind )
+ dyld::log("dyld: cache fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s) to %s\n",
+ loc, (void*)newValue, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName(), symbolName);
+ }
+ return;
+ }
+#endif
+ uintptr_t newValue =newImpl + patchLocation.getAddend();
+ if ( *loc != newValue ) {
+ *loc = newValue;
+ if ( context.verboseBind )
+ dyld::log("dyld: cache fixup: *%p = %p to %s\n", loc, (void*)newValue, symbolName);
+ }
+ });
+}
+#endif
+
+
+uintptr_t ImageLoaderMachOCompressed::resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import,
+ bool runResolver, const ImageLoader** foundIn)
+{
+ const Symbol* sym;
+#if USES_CHAINED_BINDS
+ __block uintptr_t foundOutsideCache = 0;
+ __block uintptr_t lastFoundInCache = 0;
+ CoalesceNotifier notifier = ^(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh) {
+ //dyld::log("notifier: found %s in %p %s\n", symbolName, implMh, implIn->getPath());
+ // This block is only called in dyld2 mode when a non-cached image is search for which weak-def implementation to use
+ // As a side effect of that search we notice any implementations outside and inside the cache,
+ // and use that to trigger patching the cache to use the implementation outside the cache.
+ uintptr_t implAddr = implIn->getExportedSymbolAddress(implSym, context, nullptr, false, symbolName);
+ if ( ((dyld3::MachOLoaded*)implMh)->inDyldCache() ) {
+ if ( foundOutsideCache != 0 ) {
+ // have an implementation in cache and and earlier one not in the cache, patch cache to use earlier one
+ lastFoundInCache = implAddr;
+ uint32_t imageIndex;
+ if ( context.dyldCache->findMachHeaderImageIndex(implMh, imageIndex) ) {
+ const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageIndex+1);
+ uint32_t cacheOffsetOfImpl = (uint32_t)((uintptr_t)implAddr - (uintptr_t)context.dyldCache);
+ patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, symbolName, foundOutsideCache);
+ }
+ }
+ }
+ else {
+ // record first non-cache implementation
+ if ( foundOutsideCache == 0 )
+ foundOutsideCache = implAddr;
+ }
+ };
+#else
+ CoalesceNotifier notifier = nullptr;
+#endif
+ if ( context.coalescedExportFinder(symbolName, &sym, foundIn, notifier) ) {
+ if ( *foundIn != this )
+ context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn));
+ return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
+ }
+ // if a bundle is loaded privately the above will not find its exports
+ if ( this->isBundle() && this->hasHiddenExports() ) {
+ // look in self for needed symbol
+ sym = this->findShallowExportedSymbol(symbolName, foundIn);
+ if ( sym != NULL )
+ return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
+ }
+ if ( weak_import ) {
+ // definition can't be found anywhere, ok because it is weak, just return 0
+ return 0;
+ }
+ throwSymbolNotFound(context, symbolName, this->getPath(), "", "weak");
+}
+
+
uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
const ImageLoader** foundIn)
if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) {
symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage);
}
+ else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
+ symbolAddress = this->resolveWeak(context, symbolName, false, runResolver, targetImage);
+ }
else {
if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
*targetImage = context.mainExecutable;
return symbolAddress;
}
-uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
- uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, const char* msg,
- LastLookup* last, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver)
{
const ImageLoader* targetImage;
uintptr_t symbolAddress;
// resolve symbol
- symbolAddress = this->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
+ if (type == BIND_TYPE_THREADED_REBASE) {
+ symbolAddress = 0;
+ targetImage = nullptr;
+ } else
+ symbolAddress = image->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
// do actual update
- return this->bindLocation(context, addr, symbolAddress, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, msg);
+ return image->bindLocation(context, image->imageBaseAddress(), addr, symbolAddress, type, symbolName, addend, image->getPath(), targetImage ? targetImage->getPath() : NULL, msg, extraBindData, image->fSlide);
}
// note: flat-namespace binaries need to have imports rebound (even if correctly prebound)
if ( this->usablePrebinding(context) ) {
// don't need to bind
+ // except weak which may now be inline with the regular binds
+ if (this->participatesInCoalescing()) {
+ // run through all binding opcodes
+ eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ if ( libraryOrdinal != BIND_SPECIAL_DYLIB_WEAK_LOOKUP )
+ return (uintptr_t)0;
+ return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
+ }
}
else {
uint64_t t0 = mach_absolute_time();
if ( fTextSegmentBinds )
this->makeTextSegmentWritable(context, true);
#endif
-
+
+ uint32_t ignore;
+ bool bindingBecauseOfRoot = ( this->overridesCachedDylib(ignore) || this->inSharedCache() );
+ vmAccountingSetSuspended(context, bindingBecauseOfRoot);
+
// run through all binding opcodes
- eachBind(context, &ImageLoaderMachOCompressed::bindAt);
+ eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
#if TEXT_RELOC_SUPPORT
// if there were __TEXT fixups, restore write protection
// the stub may have been altered to point to a shared lazy pointer.
if ( fInSharedCache )
this->updateOptimizedLazyPointers(context);
-
+
// tell kernel we are done with chunks of LINKEDIT
if ( !context.preFetchDisabled )
this->markFreeLINKEDIT(context);
uint64_t t1 = mach_absolute_time();
ImageLoader::fgTotalRebindCacheTime += (t1-t0);
}
+
+#if USES_CHAINED_BINDS
+ // See if this dylib overrides something in the dyld cache
+ uint32_t dyldCacheOverrideImageNum;
+ if ( context.dyldCache && overridesCachedDylib(dyldCacheOverrideImageNum) ) {
+ // need to patch all other places in cache that point to the overridden dylib, to point to this dylib instead
+ const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(dyldCacheOverrideImageNum);
+ overriddenImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* exportName) {
+ uintptr_t newImpl = 0;
+ const ImageLoader* foundIn;
+ if ( const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(exportName, &foundIn) ) {
+ newImpl = foundIn->getExportedSymbolAddress(sym, context, this);
+ }
+ patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl);
+ });
+ }
+#endif
// set up dyld entry points in image
// do last so flat main executables will have __dyld or __program_vars set up
void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context)
{
- eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
+ eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
}
-#if __arm__ || __arm64__
-int ImageLoaderMachOCompressed::vmAccountingSetSuspended(bool suspend, const LinkContext& context)
+void ImageLoaderMachOCompressed::registerInterposing(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);
+ // mach-o files advertise interposing by having a __DATA __interpose section
+ struct InterposeData { uintptr_t replacement; uintptr_t replacee; };
+ const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+ const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) {
+ // <rdar://problem/23929217> Ensure section is within segment
+ if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+ dyld::throwf("interpose section has malformed address range for %s\n", this->getPath());
+ __block uintptr_t sectionStart = sect->addr + fSlide;
+ __block uintptr_t sectionEnd = sectionStart + sect->size;
+ const size_t count = sect->size / sizeof(InterposeData);
+ InterposeData interposeArray[count];
+ // Note, we memcpy here as rebases may have already been applied.
+ memcpy(&interposeArray[0], (void*)sectionStart, sect->size);
+ __block InterposeData *interposeArrayStart = &interposeArray[0];
+ eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type,
+ const char* symbolName, uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ if (addr >= sectionStart && addr < sectionEnd) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing %s at 0x%lx in range 0x%lx..0x%lx\n",
+ symbolName, addr, sectionStart, sectionEnd);
+ }
+ const ImageLoader* targetImage;
+ uintptr_t symbolAddress;
+
+ // resolve symbol
+ if (type == BIND_TYPE_THREADED_REBASE) {
+ symbolAddress = 0;
+ targetImage = nullptr;
+ } else
+ symbolAddress = image->resolve(ctx, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
+
+ uintptr_t newValue = symbolAddress+addend;
+ uintptr_t index = (addr - sectionStart) / sizeof(uintptr_t);
+ switch (type) {
+ case BIND_TYPE_POINTER:
+ ((uintptr_t*)interposeArrayStart)[index] = newValue;
+ break;
+ case BIND_TYPE_TEXT_ABSOLUTE32:
+ // unreachable!
+ abort();
+ case BIND_TYPE_TEXT_PCREL32:
+ // unreachable!
+ abort();
+ case BIND_TYPE_THREADED_BIND:
+ ((uintptr_t*)interposeArrayStart)[index] = newValue;
+ break;
+ case BIND_TYPE_THREADED_REBASE: {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = (*(uint64_t*)addr) & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = (*(uint64_t*)addr) & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ newValue = (uintptr_t)(targetValue + fSlide);
+ ((uintptr_t*)interposeArrayStart)[index] = newValue;
+ break;
+ }
+ default:
+ dyld::throwf("bad bind type %d", type);
+ }
+ }
+ return (uintptr_t)0;
+ });
+ for (size_t j=0; j < count; ++j) {
+ ImageLoader::InterposeTuple tuple;
+ tuple.replacement = interposeArray[j].replacement;
+ tuple.neverImage = this;
+ tuple.onlyImage = NULL;
+ tuple.replacee = interposeArray[j].replacee;
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing index %d 0x%lx with 0x%lx\n",
+ (unsigned)j, interposeArray[j].replacee, interposeArray[j].replacement);
+ }
+ // <rdar://problem/25686570> ignore interposing on a weak function that does not exist
+ if ( tuple.replacee == 0 )
+ continue;
+ // <rdar://problem/7937695> verify that replacement is in this image
+ if ( this->containsAddress((void*)tuple.replacement) ) {
+ // chain to any existing interpositions
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ if ( it->replacee == tuple.replacee ) {
+ tuple.replacee = it->replacement;
+ }
+ }
+ ImageLoader::fgInterposingTuples.push_back(tuple);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
}
+
+bool ImageLoaderMachOCompressed::usesChainedFixups() const
+{
+#if __arm64e__
+ return ( machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E );
+#else
+ return false;
#endif
+}
+
+struct ThreadedBindData {
+ ThreadedBindData(const char* symbolName, int64_t addend, long libraryOrdinal, uint8_t symbolFlags, uint8_t type)
+ : symbolName(symbolName), addend(addend), libraryOrdinal(libraryOrdinal), symbolFlags(symbolFlags), type(type) { }
+
+ std::tuple<const char*, int64_t, long, bool, uint8_t> pack() const {
+ return std::make_tuple(symbolName, addend, libraryOrdinal, symbolFlags, type);
+ }
+
+ const char* symbolName = nullptr;
+ int64_t addend = 0;
+ long libraryOrdinal = 0;
+ uint8_t symbolFlags = 0;
+ uint8_t type = 0;
+};
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;
+ try {
+ const dysymtab_command* dynSymbolTable = NULL;
+ const macho_nlist* symbolTable = NULL;
+ const char* symbolTableStrings = NULL;
+ uint32_t maxStringOffset = 0;
+
+ const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SYMTAB:
+ {
+ const struct symtab_command* symtab = (struct symtab_command*)cmd;
+ symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff];
+ maxStringOffset = symtab->strsize;
+ symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]);
+ }
+ break;
+ case LC_DYSYMTAB:
+ dynSymbolTable = (struct dysymtab_command*)cmd;
+ break;
}
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
- 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;
uintptr_t address = segActualLoadAddress(0);
intptr_t addend = 0;
uintptr_t count;
uintptr_t skip;
- uintptr_t segOffset;
+ uintptr_t segOffset = 0;
+
+ dyld3::OverflowSafeArray<ThreadedBindData> ordinalTable;
+ bool useThreadedRebaseBind = false;
+ ExtraBindData extraBindData;
LastLookup last = { 0, 0, NULL, 0, NULL };
const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off;
const uint8_t* const end = &start[fDyldInfo->bind_size];
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
- throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- if ( symbolName == NULL )
- dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
- if ( segmentIndex == -1 )
- dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
- if ( !libraryOrdinalSet )
- dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
- address += sizeof(intptr_t);
+ if (!useThreadedRebaseBind) {
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
+ throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ if ( symbolName == NULL )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+ if ( segmentIndex == -1 )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+ if ( !libraryOrdinalSet )
+ dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
+ handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+ &extraBindData, "", &last, false);
+ address += sizeof(intptr_t);
+ } else {
+ ordinalTable.push_back(ThreadedBindData(symbolName, addend, libraryOrdinal, symboFlags, type));
+ }
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
if ( !libraryOrdinalSet )
dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+ handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+ &extraBindData, "", &last, false);
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
if ( !libraryOrdinalSet )
dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+ handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+ &extraBindData, "", &last, false);
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
for (uint32_t i=0; i < count; ++i) {
if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+ handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+ &extraBindData, "", &last, false);
address += skip + sizeof(intptr_t);
}
- break;
+ break;
+ case BIND_OPCODE_THREADED:
+ if (sizeof(intptr_t) != 8) {
+ dyld::throwf("BIND_OPCODE_THREADED require 64-bit");
+ return;
+ }
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ ordinalTable.clear();
+ // FIXME: ld64 wrote the wrong value here and we need to offset by 1 for now.
+ ordinalTable.reserve(count + 1);
+ useThreadedRebaseBind = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ address = segmentStartAddress + segOffset;
+ uint64_t value = *(uint64_t*)address;
+
+
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+ {
+ // Call the bind handler which knows about our bind type being set to rebase
+ handler(context, this, address, BIND_TYPE_THREADED_REBASE, nullptr, 0, 0, 0,
+ nullptr, "", &last, false);
+ }
+ } else {
+ // the ordinal is bits [0..15]
+ uint16_t ordinal = value & 0xFFFF;
+ if (ordinal >= ordinalTable.count()) {
+ dyld::throwf("bind ordinal is out of range\n");
+ return;
+ }
+ std::tie(symbolName, addend, libraryOrdinal, symboFlags, type) = ordinalTable[ordinal].pack();
+ if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
+ throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+ {
+ handler(context, this, address, BIND_TYPE_THREADED_BIND,
+ symbolName, symboFlags, addend, libraryOrdinal,
+ nullptr, "", &last, false);
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(intptr_t);
+ } while ( delta != 0 );
+ break;
+ }
+
+ default:
+ dyld::throwf("bad threaded bind subopcode 0x%02X", *p);
+ }
+ break;
default:
dyld::throwf("bad bind opcode %d in bind info", *p);
}
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
if ( symbolName == NULL )
dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false);
+ handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+ NULL, "forced lazy ", NULL, false);
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
if ( !twoLevel || context.bindFlat )
libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
uintptr_t ptrToBind = (uintptr_t)lazyPointer;
- uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL);
+ uintptr_t symbolAddr = bindAt(context, this, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal,
+ NULL, "lazy ", NULL);
++fgTotalLazyBindFixups;
return symbolAddr;
}
if ( segOffset > segSize(segIndex) )
dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex));
uintptr_t address = segActualLoadAddress(segIndex) + segOffset;
- result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
+ result = bindAt(context, this, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal,
+ NULL, "lazy ", NULL, true);
// <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once
} while (!doneAfterBind && !context.strictMachORequired);
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+ bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
boundSomething = true;
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+ bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
boundSomething = true;
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+ bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
boundSomething = true;
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+ bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
boundSomething = true;
address += skip + sizeof(intptr_t);
}
context.addDynamicReference(this, targetImage);
}
-uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
- uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char*,
+ uint8_t, intptr_t, long,
+ ExtraBindData *extraBindData,
+ const char*, LastLookup*, bool runResolver)
{
if ( type == BIND_TYPE_POINTER ) {
uintptr_t* fixupLocation = (uintptr_t*)addr;
uintptr_t curValue = *fixupLocation;
- uintptr_t newValue = interposedAddress(context, curValue, this);
+ uintptr_t newValue = interposedAddress(context, curValue, image);
if ( newValue != curValue) {
*fixupLocation = newValue;
}
dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath());
// update prebound symbols
- eachBind(context, &ImageLoaderMachOCompressed::interposeAt);
- eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt);
+ eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
+ eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
}
-uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
- uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t, intptr_t, long,
+ ExtraBindData *extraBindData,
+ const char*, LastLookup*, bool runResolver)
{
if ( type == BIND_TYPE_POINTER ) {
uintptr_t* fixupLocation = (uintptr_t*)addr;
if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) {
if ( context.verboseInterposing ) {
dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n",
- fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath());
+ fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, image->getPath());
}
*fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement;
}
dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath());
// update already bound references to symbols
- eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt);
- eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt);
+ eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
+ eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+ uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver) {
+ return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+ addend, libraryOrdinal, extraBindData,
+ msg, last, runResolver);
+ });
}
const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const
virtual bool incrementCoalIterator(CoalIterator&);
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex);
virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context);
+ virtual void registerInterposing(const LinkContext& context);
+ virtual bool usesChainedFixups() const;
protected:
virtual void doInterpose(const LinkContext& context);
#if PREBOUND_IMAGE_SUPPORT
virtual void resetPreboundLazyPointers(const LinkContext& context);
#endif
+ virtual uintptr_t resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
+ const ImageLoader** foundIn);
private:
struct LastLookup { long ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; };
- typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type,
- const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal,
- const char* msg, LastLookup* last, bool runResolver);
+ typedef uintptr_t (^bind_handler)(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type,
+ const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg, LastLookup* last, bool runResolver);
void eachLazyBind(const LinkContext& context, bind_handler);
void eachBind(const LinkContext& context, bind_handler);
void rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type);
void throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos);
- uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
- uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg,
+ static uintptr_t bindAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName,
+ uint8_t symboFlags, intptr_t addend, long libraryOrdinal,
+ ExtraBindData *extraBindData,
+ const char* msg,
LastLookup* last, bool runResolver=false);
void bindCompressed(const LinkContext& context);
void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
uintptr_t resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
const ImageLoader** foundInn);
- uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
- uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
- uintptr_t dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
- uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
+ static uintptr_t interposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char*,
+ uint8_t, intptr_t, long,
+ ExtraBindData *extraBindData,
+ const char*, LastLookup*, bool runResolver);
+ static uintptr_t dynamicInterposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char*,
+ uint8_t, intptr_t, long,
+ ExtraBindData *extraBindData,
+ const char*, LastLookup*, bool runResolver);
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
};
address += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
boundSomething = true;
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
boundSomething = true;
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
boundSomething = true;
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+ ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
boundSomething = true;
address += skip + sizeof(intptr_t);
}
}
-bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image)
+bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier)
{
+ bool found = false;
// check export trie of all in-use images
for (unsigned i=0; i < _imageCount ; ++i) {
uint16_t imageIndex = _bottomUpArray[i];
if ( _stateFlags[imageIndex] == kStateUnused )
continue;
+#if USES_CHAINED_BINDS
+ const macho_header* mh = getIndexedMachHeader(imageIndex);
+ if ( onlyInCoalesced && (mh->flags & MH_WEAK_DEFINES) == 0 )
+ continue;
+#else
if ( onlyInCoalesced && (_imageExtras[imageIndex].weakBindingsSize == 0) )
continue;
+#endif
const uint8_t* exportNode;
const uint8_t* exportTrieEnd;
if ( exportTrieHasNode(name, imageIndex, &exportNode, &exportTrieEnd) ) {
- *sym = (Symbol*)exportNode;
- *image = this;
- return true;
+ if ( notifier )
+ notifier((Symbol*)exportNode, this, (mach_header*)getIndexedMachHeader(imageIndex));
+ if ( !found ) {
+ *sym = (Symbol*)exportNode;
+ *image = this;
+ found = true;
+ }
+ if ( !onlyInCoalesced )
+ return true;
}
}
- return false;
+ return found;
}
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, getIndexedPath(imageIndex));
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
- dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+ {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)getIndexedMachHeader(imageIndex), (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 ) {
#define __IMAGELOADER_MEGADYLIB__
#include <stdint.h>
-#include <pthread.h>
+#include <pthread.h>
+#include <uuid/uuid.h>
#include "ImageLoaderMachO.h"
#include "dyld_cache_format.h"
virtual const char* getInstallPath() const;
virtual bool inSharedCache() const { return true; }
virtual bool containsSymbol(const void* addr) const { unreachable(); }
- virtual void* getThreadPC() const { unreachable(); }
- virtual void* getMain() const { unreachable(); }
+ virtual void* getEntryFromLC_MAIN() const { unreachable(); }
+ virtual void* getEntryFromLC_UNIXTHREAD() const { unreachable(); }
virtual const struct mach_header* machHeader() const { unreachable(); }
virtual uintptr_t getSlide() const { return _slide; }
virtual const void* getEnd() const { unreachable(); }
virtual bool needsInitialization() { unreachable(); }
virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) { unreachable(); }
virtual void getUnwindInfo(dyld_unwind_sections* info) { unreachable(); }
- virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); }
+ virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); }
+ virtual const struct macho_section* findSection(const void* imageInterior) const { unreachable(); }
virtual bool isPrebindable() const { unreachable(); }
virtual bool usablePrebinding(const LinkContext& context) const { unreachable(); }
virtual void getRPaths(const LinkContext& context, std::vector<const char*>&) const { }
virtual uint32_t minOSVersion() const { unreachable(); }
// if the image contains interposing functions, register them
- virtual void registerInterposing() { unreachable(); }
+ virtual void registerInterposing(const LinkContext& context) { unreachable(); }
virtual ImageLoader* libImage(unsigned int) const { unreachable(); }
virtual bool libReExported(unsigned int) const { unreachable(); }
bool findUnwindSections(const void* addr, dyld_unwind_sections* info);
bool dladdrFromCache(const void* address, Dl_info* info);
uintptr_t bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned index);
- bool flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image);
+ bool flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier);
void getDylibUUID(unsigned int index, uuid_t) const;
protected:
#include <sys/un.h>
#include <sys/syslog.h>
#include <sys/uio.h>
-#include <sys/xattr.h>
#include <mach/mach.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <System/machine/cpu_capabilities.h>
#include <System/sys/reason.h>
#include <kern/kcdata.h>
+#if TARGET_IPHONE_SIMULATOR
+ enum {
+ AMFI_DYLD_INPUT_PROC_IN_SIMULATOR = (1 << 0),
+ };
+ enum amfi_dyld_policy_output_flag_set {
+ AMFI_DYLD_OUTPUT_ALLOW_AT_PATH = (1 << 0),
+ AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS = (1 << 1),
+ AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE = (1 << 2),
+ AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS = (1 << 3),
+ AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS = (1 << 4),
+ AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION = (1 << 5),
+ };
+ extern "C" int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags);
+#else
+ #include <libamfi.h>
+#endif
+extern "C" {
+ #include <corecrypto/ccdigest.h>
+ #include <corecrypto/ccsha2.h>
+}
#include <sandbox.h>
#include <sandbox/private.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
extern "C" int __fork();
#define CPU_SUBTYPE_ARM64_E 2
#endif
+#ifndef CPU_ARCH_ABI64_32
+ #define CPU_ARCH_ABI64_32 ((cpu_type_t) 0x02000000)
+#endif
+#ifndef CPU_TYPE_ARM64_32
+ #define CPU_TYPE_ARM64_32 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64_32))
+#endif
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+ #define CPU_SUBTYPE_ARM64_32_V8 ((cpu_subtype_t) 1)
+#endif
+
#ifndef VM_PROT_SLIDE
#define VM_PROT_SLIDE 0x20
#endif
#include "dyldLibSystemInterface.h"
#include "dyld_cache_format.h"
#include "dyld_process_info_internal.h"
-#include <coreSymbolicationDyldSupport.h>
-#if TARGET_IPHONE_SIMULATOR
- extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
- extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
- #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h)
- #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h)
-#endif
#if SUPPORT_ACCELERATE_TABLES
#include "ImageLoaderMegaDylib.h"
#include "dyldSyscallInterface.h"
#endif
-#include "LaunchCache.h"
+#include "Closure.h"
#include "libdyldEntryVector.h"
-#include "MachOParser.h"
+#include "MachOLoaded.h"
#include "Loading.h"
#include "DyldSharedCache.h"
#include "SharedCacheRuntime.h"
#include "StringUtils.h"
#include "Tracing.h"
-#include "DyldCacheParser.h"
-
-extern "C" {
- #include "closuredProtocol.h"
-}
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
+#include "FileUtils.h"
// not libc header for send() syscall interface
bool DYLD_PRINT_OPTS;
bool DYLD_PRINT_ENV;
bool DYLD_DISABLE_DOFS;
- bool DYLD_PRINT_CS_NOTIFICATIONS;
// DYLD_SHARED_CACHE_DIR ==> sSharedCacheOverrideDir
// DYLD_ROOT_PATH ==> gLinkContext.rootPaths
// DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix
static cpu_subtype_t sHostCPUsubtype;
#endif
static ImageLoaderMachO* sMainExecutable = NULL;
-static EnvVarMode sEnvMode = envNone;
static size_t sInsertedDylibCount = 0;
static std::vector<ImageLoader*> sAllImages;
static std::vector<ImageLoader*> sImageRoots;
static std::vector<RegisteredDOF> sImageFilesNeedingDOFUnregistration;
static std::vector<ImageCallback> sAddImageCallbacks;
static std::vector<ImageCallback> sRemoveImageCallbacks;
+static std::vector<LoadImageCallback> sAddLoadImageCallbacks;
static bool sRemoveImageCallbacksInUse = false;
static void* sSingleHandlers[7][3];
static void* sBatchHandlers[7][3];
static bool sLogToFile = false;
#endif
static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries";
-static bool sSafeMode = false;
static _dyld_objc_notify_mapped sNotifyObjCMapped;
static _dyld_objc_notify_init sNotifyObjCInit;
static _dyld_objc_notify_unmapped sNotifyObjCUnmapped;
bool gUseDyld3 = false;
static bool sSkipMain = false;
static bool sEnableClosures = false;
+static uint64_t launchTraceID = 0;
//
// The MappedRanges structure is used for fast address->image lookups.
{
// use guard so that we cannot notify about the same image twice
if ( ! image->addFuncNotified() ) {
- for (std::vector<ImageCallback>::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++)
+ for (std::vector<ImageCallback>::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*it), 0);
(*it)(image->machHeader(), image->getSlide());
+ }
+ for (LoadImageCallback func : sAddLoadImageCallbacks) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
+ (*func)(image->machHeader(), image->getPath(), !image->neverUnload());
+ }
image->setAddFuncNotified();
}
}
}
}
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)mh, 0, 0);
(*sNotifyObjCInit)(path, mh);
}
}
#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] )
+#if !TARGET_OS_SIMULATOR
+static void sendMessage(unsigned portSlot, mach_msg_id_t msgId, mach_msg_size_t sendSize, mach_msg_header_t* buffer, mach_msg_size_t bufferSize) {
+ // Allocate a port to listen on in this monitoring task
+ mach_port_t sendPort = dyld::gProcessInfo->notifyPorts[portSlot];
+ if (sendPort == MACH_PORT_NULL) {
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) + 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 = imageCount/2;
- notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos);
- notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]);
+ }
+ mach_port_t replyPort = MACH_PORT_NULL;
+ mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+ .mpl = { 1 }};
+ kern_return_t kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)&replyPort, &replyPort);
+ if (kr != KERN_SUCCESS) {
return;
}
- uint8_t buffer[totalSize];
- dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
- header->version = 1;
- header->imageCount = imageCount;
- header->imagesOffset = sizeof(dyld_process_info_notify_header);
- header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize;
- header->timestamp = dyld::gProcessInfo->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 (unsigned j=0; j < imageCount; ++j) {
- strcpy(pathPool, infos[j].imageFilePath);
- uint32_t len = (uint32_t)strlen(pathPool);
- bzero(entries->uuid, 16);
- const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress);
- if ( image != NULL ) {
- image->getUUID(entries->uuid);
+ // Assemble a message
+ mach_msg_header_t* h = buffer;
+ h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ h->msgh_id = msgId;
+ h->msgh_local_port = replyPort;
+ h->msgh_remote_port = sendPort;
+ h->msgh_reserved = 0;
+ h->msgh_size = sendSize;
+ kr = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG, h->msgh_size, bufferSize, replyPort, 0, MACH_PORT_NULL);
+ mach_msg_destroy(h);
+ if ( kr == MACH_SEND_INVALID_DEST ) {
+ if (OSAtomicCompareAndSwap32(sendPort, 0, (volatile int32_t*)&dyld::gProcessInfo->notifyPorts[portSlot])) {
+ mach_port_deallocate(mach_task_self(), sendPort);
}
-#if SUPPORT_ACCELERATE_TABLES
- else if ( sAllCacheImagesProxy != NULL ) {
- const mach_header* mh;
- const char* path;
- unsigned index;
- if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) {
- sAllCacheImagesProxy->getDylibUUID(index, entries->uuid);
- }
+ }
+ mach_port_destruct(mach_task_self(), replyPort, 0, (mach_port_context_t)&replyPort);
+}
+
+static void notifyMonitoringDyld(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+ const char* imagePaths[])
+{
+ dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER, 0, 0, 0);
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( dyld::gProcessInfo->notifyPorts[slot] == 0) continue;
+ unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry);
+ unsigned pathsSize = 0;
+ for (unsigned j=0; j < imageCount; ++j) {
+ pathsSize += (strlen(imagePaths[j]) + 1);
+ }
+ unsigned totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align
+ if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) {
+ // Putting all image paths into one message would make buffer too big.
+ // Instead split into two messages. Recurse as needed until paths fit in buffer.
+ unsigned imageHalfCount = imageCount/2;
+ notifyMonitoringDyld(unloading, imageHalfCount, loadAddresses, imagePaths);
+ notifyMonitoringDyld(unloading, imageCount - imageHalfCount, &loadAddresses[imageHalfCount], &imagePaths[imageHalfCount]);
+ return;
+ }
+ uint8_t buffer[totalSize + MAX_TRAILER_SIZE];
+ dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
+ header->version = 1;
+ header->imageCount = imageCount;
+ header->imagesOffset = sizeof(dyld_process_info_notify_header);
+ header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize;
+ header->timestamp = dyld::gProcessInfo->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 (unsigned j=0; j < imageCount; ++j) {
+ strcpy(pathPool, imagePaths[j]);
+ uint32_t len = (uint32_t)strlen(pathPool);
+ bzero(entries->uuid, 16);
+ dyld3::MachOFile* mf = (dyld3::MachOFile*)loadAddresses[j];
+ mf->getUuid(entries->uuid);
+ entries->loadAddress = (uint64_t)loadAddresses[j];
+ entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
+ entries->pathLength = len;
+ pathPool += (len +1);
+ ++entries;
+ }
+ if (unloading) {
+ sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
+ } else {
+ sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
}
-#endif
- entries->loadAddress = (uint64_t)infos[j].imageLoadAddress;
- entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
- entries->pathLength = len;
- pathPool += (len +1);
- ++entries;
- }
-
- if ( sNotifyReplyPorts[portSlot] == 0 ) {
- if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) )
- mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND);
- //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
- }
- //dyld::log("found port to send to\n");
- mach_msg_header_t* h = (mach_msg_header_t*)buffer;
- h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
- h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID;
- h->msgh_local_port = sNotifyReplyPorts[portSlot];
- h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot];
- h->msgh_reserved = 0;
- h->msgh_size = (mach_msg_size_t)sizeof(buffer);
- //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
- kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_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::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
- mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]);
- mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
- dyld::gProcessInfo->notifyPorts[portSlot] = 0;
- sNotifyReplyPorts[portSlot] = 0;
- }
- 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()
{
+ dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER, 0, 0, 0);
for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
- 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");
- 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]);
- mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[slot]);
- mach_port_deallocate(mach_task_self(), 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;
- }
- }
+ if ( dyld::gProcessInfo->notifyPorts[slot] == 0) continue;
+ uint8_t buffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+ sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_MAIN_ID, sizeof(mach_msg_header_t), (mach_msg_header_t*)buffer, sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE);
}
}
+#else
+extern void notifyMonitoringDyldMain() VIS_HIDDEN;
+extern void notifyMonitoringDyld(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+ const char* imagePaths[]) VIS_HIDDEN;
+#endif
void notifyKernel(const ImageLoader& image, bool loading) {
if ( !image.inSharedCache() ) {
}
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
// mach message csdlc about dynamically unloaded images
if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
notifyKernel(*image, false);
-
- uint64_t loadTimestamp = mach_absolute_time();
- if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
- dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n",
- dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath());
- }
- if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) {
- coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader());
- }
- for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
- if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) {
- dyld_image_info info;
- info.imageLoadAddress = image->machHeader();
- info.imageFilePath = image->getPath();
- info.imageFileModDate = 0;
- notifyMonitoringDyld(true, slot, 1, &info);
- }
- else if ( sNotifyReplyPorts[slot] != 0 ) {
- // monitoring process detached from this process, so release reply port
- //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
- mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
- sNotifyReplyPorts[slot] = 0;
- sZombieNotifiers[slot] = false;
- }
- }
+ const struct mach_header* loadAddress[] = { image->machHeader() };
+ const char* loadPath[] = { image->getPath() };
+ notifyMonitoringDyld(true, 1, loadAddress, loadPath);
}
-
}
// support _dyld_register_func_for_add_image()
if ( state == dyld_image_state_bound ) {
for (ImageCallback callback : sAddImageCallbacks) {
- for (unsigned i=0; i < cacheCount; ++i)
+ for (unsigned i=0; i < cacheCount; ++i) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*callback), 0);
(*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
+ }
+ }
+ for (LoadImageCallback func : sAddLoadImageCallbacks) {
+ for (unsigned i=0; i < cacheCount; ++i) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*func), 0);
+ (*func)(infos[imageCount+i].imageLoadAddress, infos[imageCount+i].imageFilePath, false);
+ }
}
}
imageCount += cacheCount;
}
}
if ( objcImageCount != 0 ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
uint64_t t0 = mach_absolute_time();
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
uint64_t t1 = mach_absolute_time();
if ( dontLoadReason != NULL )
throw dontLoadReason;
if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) {
- if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
- // mach message csdlc about loaded images
- uint64_t loadTimestamp = mach_absolute_time();
- for (unsigned j=0; j < imageCount; ++j) {
- if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
- dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n",
- dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath);
- }
- coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress);
- }
- }
- for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
- if ( dyld::gProcessInfo->notifyPorts[slot] )
- notifyMonitoringDyld(false, slot, imageCount, infos);
+ const struct mach_header* loadAddresses[imageCount];
+ const char* loadPaths[imageCount];
+ for(uint32_t i = 0; i<imageCount; ++i) {
+ loadAddresses[i] = infos[i].imageLoadAddress;
+ loadPaths[i] = infos[i].imageFilePath;
}
+ notifyMonitoringDyld(false, imageCount, loadAddresses, loadPaths);
}
}
}
-
-
static void notifyBatch(dyld_image_states state, bool preflightOnly)
{
notifyBatchPartial(state, false, NULL, preflightOnly, false);
}
+static
+void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+ const struct mach_header* loadAddress[] = { mh };
+ const char* loadPath[] = { path };
+ notifyMonitoringDyld(false, 1, loadAddress, loadPath);
+}
+
+static
+void coresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+ const struct mach_header* loadAddress = { mh };
+ const char* loadPath = { path };
+ notifyMonitoringDyld(true, 1, &loadAddress, &loadPath);
+}
+
// In order for register_func_for_add_image() callbacks to to be called bottom up,
// we need to maintain a list of root images. The main executable is usally the
// first root. Any images dynamically added are also roots (unless already loaded).
if ( image->getState() >= dyld_image_state_bound ) {
sRemoveImageCallbacksInUse = true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag.
for (std::vector<ImageCallback>::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*it), 0);
(*it)(image->machHeader(), image->getSlide());
}
sRemoveImageCallbacksInUse = false;
if (*s == ':') {
size_t len = s-start;
if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
- dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+ if ( !gLinkContext.allowAtPaths ) {
+ dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy\n");
continue;
}
-#endif
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
strcpy(str, mainExecutableDir);
result[index++] = str;
}
else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
- dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+ if ( !gLinkContext.allowAtPaths ) {
+ dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy\n");
continue;
}
-#endif
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
strcpy(str, mainExecutableDir);
}
size_t len = strlen(start);
if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
- dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+ if ( !gLinkContext.allowAtPaths ) {
+ dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy\n");
}
else
-#endif
{
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
}
}
else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
- dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+ if ( !gLinkContext.allowAtPaths ) {
+ dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy\n");
}
else
-#endif
{
size_t mainExecDirLen = strlen(mainExecutableDir);
char* str = new char[mainExecDirLen+len+1];
}
#endif
else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
- gLinkContext.imageSuffix = value;
+ gLinkContext.imageSuffix = parseColonList(value, NULL);
}
else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL);
else if ( strcmp(key, "DYLD_PRINT_RPATHS") == 0 ) {
gLinkContext.verboseRPaths = true;
}
- else if ( strcmp(key, "DYLD_PRINT_CS_NOTIFICATIONS") == 0 ) {
- sEnv.DYLD_PRINT_CS_NOTIFICATIONS = true;
- }
else if ( strcmp(key, "DYLD_PRINT_INTERPOSING") == 0 ) {
gLinkContext.verboseInterposing = true;
}
else if ( strcmp(key, "DYLD_PRINT_CODE_SIGNATURES") == 0 ) {
gLinkContext.verboseCodeSignatures = true;
}
- else if ( (strcmp(key, "DYLD_SHARED_REGION") == 0) && !sSafeMode ) {
+ else if ( (strcmp(key, "DYLD_SHARED_REGION") == 0) && gLinkContext.allowEnvVarsSharedCache ) {
if ( strcmp(value, "private") == 0 ) {
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
}
dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n");
}
}
- else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && !sSafeMode ) {
+ else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && gLinkContext.allowEnvVarsSharedCache ) {
sSharedCacheOverrideDir = value;
}
else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) {
- if ( dyld3::loader::internalInstall() )
+ if ( dyld3::internalInstall() ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && __i386__
+ // don't support dyld3 for 32-bit macOS
+#else
sEnableClosures = true;
+#endif
+ }
}
else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
if ( strcmp(value, "all") == 0 ) {
}
#endif
#if !TARGET_IPHONE_SIMULATOR
- else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && !sSafeMode ) {
+ else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && gLinkContext.allowEnvVarsSharedCache ) {
int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644);
if ( fd != -1 ) {
sLogfile = fd;
}
}
else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) {
- if ( dyld3::loader::internalInstall() )
+ if ( dyld3::internalInstall() )
sSkipMain = true;
}
#endif
// Are we testing dyld on an internal config?
if ( _simple_getenv(envp, "DYLD_SKIP_MAIN") != NULL ) {
- if ( dyld3::loader::internalInstall() )
+ if ( dyld3::internalInstall() )
sSkipMain = true;
}
static void defaultUninitializedFallbackPaths(const char* envp[])
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
+ if ( !gLinkContext.allowClassicFallbackPaths ) {
sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths;
return;
static void checkEnvironmentVariables(const char* envp[])
{
- if ( sEnvMode == envNone )
+ if ( !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsPrint )
return;
const char** p;
for(p = envp; *p != NULL; p++) {
char key[keyLen+1];
strncpy(key, keyEqualsValue, keyLen);
key[keyLen] = '\0';
- if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) )
+ if ( (strncmp(key, "DYLD_PRINT_", 11) == 0) && !gLinkContext.allowEnvVarsPrint )
continue;
processDyldEnvironmentVariable(key, value, NULL);
}
#if SUPPORT_ROOT_PATH
// <rdar://problem/11281064> DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
- if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
+ if ( (gLinkContext.imageSuffix != NULL && *gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
- gLinkContext.imageSuffix = NULL;
+ gLinkContext.imageSuffix = NULL; // this leaks allocations from parseColonList
}
#endif
}
#elif __ARM_ARCH_7S__
sHostCPU = CPU_TYPE_ARM;
sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S;
+#elif __ARM64_ARCH_8_32__
+ sHostCPU = CPU_TYPE_ARM64_32;
+ sHostCPUsubtype = CPU_SUBTYPE_ARM64_32_V8;
#elif __arm64e__
sHostCPU = CPU_TYPE_ARM64;
sHostCPUsubtype = CPU_SUBTYPE_ARM64_E;
#endif
}
-static void checkSharedRegionDisable(const mach_header* mainExecutableMH)
+static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide)
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// if main executable has segments that overlap the shared region,
// then disable using the shared region
- 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 ) {
+ if ( mainExecutableMH->intersectsRange(SHARED_REGION_BASE, SHARED_REGION_SIZE) ) {
gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
if ( gLinkContext.verboseMapping )
dyld::warn("disabling shared region because main executable overlaps\n");
}
#if __i386__
- if ( gLinkContext.processIsRestricted ) {
+ if ( !gLinkContext.allowEnvVarsPath ) {
// <rdar://problem/15280847> use private or no shared region for suid processes
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
}
if ( gLinkContext.imageSuffix != NULL ) {
// some debug frameworks have install names that end in _debug
if ( strncmp(framework, &leaf[1], len) == 0 ) {
- if ( strcmp( gLinkContext.imageSuffix, &leaf[len+1]) == 0 )
- return frameworkStart;
+ for (const char* const* suffix=gLinkContext.imageSuffix; *suffix != NULL; ++suffix) {
+ if ( strcmp(*suffix, &leaf[len+1]) == 0 )
+ return frameworkStart;
+ }
}
}
}
// armv64 can run: 64
{ CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
};
+
+#if __ARM64_ARCH_8_32__
+const int kARM64_32_RowCount = 2;
+static const cpu_subtype_t kARM64_32[kARM64_32_RowCount][4] = {
+
+ // armv64_32 can run: v8
+ { CPU_SUBTYPE_ARM64_32_V8, CPU_SUBTYPE_END_OF_LIST },
+
+ // armv64 can run: 64
+ { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+};
+#endif
+
#endif
#if __x86_64__
return kARM64[i];
}
break;
+
+#if __ARM64_ARCH_8_32__
+ case CPU_TYPE_ARM64_32:
+ for (int i=0; i < kARM64_32_RowCount ; ++i) {
+ if ( kARM64_32[i][0] == subtype )
+ return kARM64_32[i];
+ }
+ break;
+#endif
+
#endif
#if __x86_64__
case CPU_TYPE_X86_64:
#if SUPPORT_ACCELERATE_TABLES
static bool dylibsCanOverrideCache()
{
- if ( !dyld3::loader::internalInstall() )
+ if ( !dyld3::internalInstall() )
return false;
return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeDevelopment) );
}
// grandfather in a few libSystem dylibs
if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
(strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
- (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0))
+ (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0))
return true;
return false;
+ case LC_BUILD_VERSION:
+ {
+ // Same logic as above, but for LC_BUILD_VERSION instead of legacy load commands
+ const struct build_version_command* buildVersionCmd = (build_version_command*)cmd;
+ switch(buildVersionCmd->platform) {
+ case PLATFORM_IOSSIMULATOR:
+ case PLATFORM_TVOSSIMULATOR:
+ case PLATFORM_WATCHOSSIMULATOR:
+ case PLATFORM_WATCHOS:
+ return true;
+ #if TARGET_OS_IOSMAC
+ case 6:
+ return true;
+ #endif
+ case PLATFORM_MACOS:
+ if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
+ (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0))
+ return true;
+ }
+ }
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
if ( cmd > cmdsEnd )
}
#endif
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static bool iOSMacWhiteListed(const char* path)
+{
+ static char* whiteListBuffer = nullptr;
+ static size_t whiteListSize = 0;
+ static bool tried = false;
+ if ( !tried ) {
+ // only try to map file once
+ whiteListBuffer = (char*)mapFileReadOnly("/System/iOSSupport/dyld/macOS-whitelist.txt", whiteListSize);
+ tried = true;
+ }
+ __block bool result = false;
+ if ( whiteListBuffer != nullptr ) {
+ dyld3::forEachLineInFile(whiteListBuffer, whiteListSize, ^(const char* line, bool& stop) {
+ // lines in the file are prefixes. Any path that starts with one of these lines is allowed to be unzippered
+ size_t lineLen = strlen(line);
+ if ( (*line == '/') && strncmp(line, path, lineLen) == 0 ) {
+ result = true;
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+static bool iOSMacBlackListed(const char* path)
+{
+ static char* blackListBuffer = nullptr;
+ static size_t blackListSize = 0;
+ static bool tried = false;
+ if ( !tried ) {
+ // only try to map file once
+ blackListBuffer = (char*)mapFileReadOnly("/System/iOSSupport/dyld/macOS-blacklist.txt", blackListSize);
+ tried = true;
+ }
+ __block bool result = false;
+ if ( blackListBuffer != nullptr ) {
+ dyld3::forEachLineInFile(blackListBuffer, blackListSize, ^(const char* line, bool& stop) {
+ // lines in the file are prefixes. Any path that starts with one of these lines is allowed to be unzippered
+ size_t lineLen = strlen(line);
+ if ( (*line == '/') && strncmp(line, path, lineLen) == 0 ) {
+ result = true;
+ stop = true;
+ }
+ });
+ }
+ return result;
+}
+
+
+#endif
+
// map in file and instantiate an ImageLoader
static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context)
{
throw "not a file";
uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE];
+ uint8_t *firstPagesPtr = firstPages;
bool shortPage = false;
// min mach-o file is 4K
// try mach-o loader
if ( shortPage )
throw "file too short";
+
if ( isCompatibleMachO(firstPages, path) ) {
// only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
}
#endif
- // instantiate an image
- ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.marzipan ) {
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages;
+ bool isiOSMacBinary = mf->supportsPlatform(dyld3::Platform::iOSMac) || iOSMacWhiteListed(path);
+ bool isProhibitedMacOSBinary = !isiOSMacBinary && iOSMacBlackListed(path);
+ if ( (context.enforceIOSMac && !isiOSMacBinary) || isProhibitedMacOSBinary ) {
+ throw "mach-o, but not built for iOSMac";
+ }
+ }
+#endif
+
+#if __arm64e__
+ if ( (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64_E) && (mh->cpusubtype != CPU_SUBTYPE_ARM64_E) )
+ throw "arm64 dylibs cannot be loaded into arm64e processes";
+#endif
+ ImageLoader* image = nullptr;
+ {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, path, 0, 0);
+ image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPagesPtr, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
+ timer.setData4((uint64_t)image->machHeader());
+ }
// validate
return checkandAddImage(image, context);
bool didStat = false;
bool existsOnDisk;
dyld3::SharedCacheFindDylibResults shareCacheResults;
+ shareCacheResults.image = nullptr;
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) {
return NULL;
}
bool useCache = false;
- if ( shareCacheResults.imageData == nullptr ) {
+ if ( shareCacheResults.image == nullptr ) {
// HACK to support old caches
existsOnDisk = ( my_stat(path, &statBuf) == 0 );
didStat = true;
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() ) {
+ if ( shareCacheResults.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;
+ uint64_t expectedINode;
+ uint64_t expectedMtime;
+ if ( shareCacheResults.image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) {
+ if ( (expectedMtime == statBuf.st_mtime) && (expectedINode == statBuf.st_ino) )
+ useCache = true;
+ }
}
else {
if ( !existsOnDisk )
}
}
if ( useCache ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.marzipan ) {
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)shareCacheResults.mhInCache;
+ bool isiOSMacBinary = mf->supportsPlatform(dyld3::Platform::iOSMac) || iOSMacWhiteListed(path);
+ bool isProhibitedMacOSBinary = !isiOSMacBinary && iOSMacBlackListed(path);
+ if ( (context.enforceIOSMac && !isiOSMacBinary) || isProhibitedMacOSBinary ) {
+ throw "mach-o, but not built for iOSMac";
+ }
+ }
+#endif
ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
return checkandAddImage(imageLoader, context);
}
return NULL;
// try opening file
imageLoader = loadPhase5open(path, context, statBuf, exceptions);
- if ( imageLoader != NULL )
+ if ( imageLoader != NULL ) {
+ if ( shareCacheResults.image != nullptr ) {
+ // if image was found in cache, but is overridden by a newer file on disk, record what the image overrides
+ imageLoader->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+ }
return imageLoader;
+ }
}
// just return NULL if file not found, but record any other errors
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
- if ( gLinkContext.imageSuffix != NULL ) {
- char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2];
- ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix);
- image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions);
+ if ( gLinkContext.imageSuffix != NULL ) {
+ for (const char* const* suffix=gLinkContext.imageSuffix; *suffix != NULL; ++suffix) {
+ char pathWithSuffix[strlen(path)+strlen(*suffix)+2];
+ ImageLoader::addSuffix(path, *suffix, pathWithSuffix);
+ image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions);
+ if ( image != NULL )
+ break;
+ }
+ if ( image != NULL ) {
+ // if original path is in the dyld cache, then mark this one found as an override
+ dyld3::SharedCacheFindDylibResults shareCacheResults;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+ image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+ }
}
if ( image == NULL )
image = loadPhase5(path, orgPath, context, cacheIndex, exceptions);
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
if ( strncmp(path, "@executable_path/", 17) == 0 ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
// executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305
- if ( gLinkContext.processIsRestricted )
+ if ( !gLinkContext.allowAtPaths )
throwf("unsafe use of @executable_path in %s with restricted binary", context.origin);
-#endif
// handle @executable_path path prefix
const char* executablePath = sExecPath;
char newPath[strlen(executablePath) + strlen(path)];
}
}
else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
// @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305
- if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) )
+ if ( !gLinkContext.allowAtPaths && (strcmp(context.origin, sExecPath) == 0) )
throwf("unsafe use of @loader_path in %s with restricted binary", context.origin);
-#endif
// handle @loader_path path prefix
char newPath[strlen(context.origin) + strlen(path)];
strcpy(newPath, context.origin);
if ( (exceptions != NULL) && (trailingPath != path) )
return NULL;
}
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) {
+ else if ( !gLinkContext.allowEnvVarsPath && (path[0] != '/' ) ) {
throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin);
}
-#endif
-
+
return loadPhase4(path, orgPath, context, cacheIndex, exceptions);
}
// Look in the cache if appropriate
if ( image == NULL)
image = loadPhase2cache(npath, orgPath, context, cacheIndex, exceptions);
- if ( image != NULL )
+ if ( image != NULL ) {
+ // if original path is in the dyld cache, then mark this one found as an override
+ dyld3::SharedCacheFindDylibResults shareCacheResults;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+ image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
return image;
+ }
}
}
}
// Look in the cache if appropriate
if ( image == NULL)
image = loadPhase2cache(libpath, orgPath, context, cacheIndex, exceptions);
- if ( image != NULL )
+ if ( image != NULL ) {
+ // if original path is in the dyld cache, then mark this one found as an override
+ dyld3::SharedCacheFindDylibResults shareCacheResults;
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+ image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
return image;
+ }
}
}
return NULL;
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // handle macOS dylibs dlopen()ing versioned path which needs to map to flat path in mazipan simulator
+ if ( gLinkContext.marzipan && strstr(path, ".framework/Versions/")) {
+ uintptr_t sourceOffset = 0;
+ uintptr_t destOffset = 0;
+ size_t sourceLangth = strlen(path);
+ char flatPath[sourceLangth];
+ flatPath[0] = 0;
+ const char* frameworkBase = NULL;
+ while ((frameworkBase = strstr(&path[sourceOffset], ".framework/Versions/"))) {
+ uintptr_t foundLength = (frameworkBase - &path[sourceOffset]) + strlen(".framework/") ;
+ strlcat(&flatPath[destOffset], &path[sourceOffset], foundLength);
+ sourceOffset += foundLength + strlen("Versions/") + 1;
+ destOffset += foundLength - 1;
+ }
+ strlcat(&flatPath[destOffset], &path[sourceOffset], sourceLangth);
+ ImageLoader* image = loadPhase0(flatPath, orgPath, context, cacheIndex, exceptions);
+ if ( image != NULL )
+ return image;
+ }
+#endif
+
#if SUPPORT_ROOT_PATH
// handle DYLD_ROOT_PATH which forces absolute paths to use a new root
if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) {
- for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) {
- char newPath[strlen(*rootPath) + strlen(path)+2];
- strcpy(newPath, *rootPath);
- strcat(newPath, path);
- ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions);
- if ( image != NULL )
- return image;
+ for(const char* const* rootPath = gLinkContext.rootPaths; *rootPath != NULL; ++rootPath) {
+ size_t rootLen = strlen(*rootPath);
+ if ( strncmp(path, *rootPath, rootLen) != 0 ) {
+ char newPath[rootLen + strlen(path)+2];
+ strcpy(newPath, *rootPath);
+ strcat(newPath, path);
+ ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions);
+ if ( image != NULL )
+ return image;
+ }
}
}
#endif
//dyld::log("%s(%s)\n", __func__ , path);
char realPath[PATH_MAX];
// when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match
- if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) {
+ if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL && *gLinkContext.imageSuffix != NULL) ) {
if ( realpath(path, realPath) != NULL )
path = realPath;
}
dyld3::SharedCacheOptions opts;
opts.cacheDirOverride = sSharedCacheOverrideDir;
opts.forcePrivate = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion);
+
+
#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
opts.useHaswell = sHaswell;
#else
// update global state
if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ gLinkContext.dyldCache = sSharedCacheLoadInfo.loadAddress;
dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate;
dyld::gProcessInfo->sharedCacheSlide = sSharedCacheLoadInfo.slide;
dyld::gProcessInfo->sharedCacheBaseAddress = (unsigned long)sSharedCacheLoadInfo.loadAddress;
context.mustBeBundle = true;
context.mustBeDylib = false;
context.canBePIE = false;
+ context.enforceIOSMac = false;
context.origin = NULL;
context.rpath = NULL;
return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context);
// call callback with all existing images
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
- if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated )
+ if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
(*func)(image->machHeader(), image->getSlide());
+ }
}
#if SUPPORT_ACCELERATE_TABLES
if ( sAllCacheImagesProxy != NULL ) {
dyld_image_info infos[allImagesCount()+1];
unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
for (unsigned i=0; i < cacheCount; ++i) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[i].imageLoadAddress, (uint64_t)(*func), 0);
(*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide);
}
}
#endif
}
+void registerLoadCallback(LoadImageCallback func)
+{
+ // now add to list to get notified when any more images are added
+ sAddLoadImageCallbacks.push_back(func);
+
+ // call callback with all existing images
+ for (ImageLoader* image : sAllImages) {
+ if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
+ (*func)(image->machHeader(), image->getPath(), !image->neverUnload());
+ }
+ }
+#if SUPPORT_ACCELERATE_TABLES
+ if ( sAllCacheImagesProxy != NULL ) {
+ dyld_image_info infos[allImagesCount()+1];
+ unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
+ for (unsigned i=0; i < cacheCount; ++i) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[i].imageLoadAddress, (uint64_t)(*func), 0);
+ (*func)(infos[i].imageLoadAddress, infos[i].imageFilePath, false);
+ }
+ }
+#endif
+}
+
void registerRemoveCallback(ImageCallback func)
{
// <rdar://problem/15025198> ignore calls to register a notification during a notification
}
}
-static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image)
+static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier=NULL)
{
// search all images in order
const ImageLoader* firstWeakImage = NULL;
const ImageLoader::Symbol* firstWeakSym = NULL;
+ const ImageLoader* firstNonWeakImage = NULL;
+ const ImageLoader::Symbol* firstNonWeakSym = NULL;
const size_t imageCount = sAllImages.size();
for(size_t i=0; i < imageCount; ++i) {
ImageLoader* anImage = sAllImages[i];
else if ( i == sInsertedDylibCount )
anImage = sAllImages[0];
}
+ //dyld::log("findExportedSymbol(%s) looking at %s\n", name, anImage->getPath());
if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) {
- *sym = anImage->findExportedSymbol(name, false, image);
+ const ImageLoader* foundInImage;
+ *sym = anImage->findExportedSymbol(name, false, &foundInImage);
+ //dyld::log("findExportedSymbol(%s) found: sym=%p, anImage=%p, foundInImage=%p\n", name, *sym, anImage, foundInImage /*, (foundInImage ? foundInImage->getPath() : "")*/);
if ( *sym != NULL ) {
+ if ( notifier && (foundInImage == anImage) )
+ notifier(*sym, foundInImage, foundInImage->machHeader());
// if weak definition found, record first one found
- if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) {
+ if ( (foundInImage->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) {
if ( firstWeakImage == NULL ) {
- firstWeakImage = *image;
+ firstWeakImage = foundInImage;
firstWeakSym = *sym;
}
}
else {
- // found non-weak, so immediately return with it
- return true;
+ // found non-weak
+ if ( !onlyInCoalesced ) {
+ // for flat lookups, return first found
+ *image = foundInImage;
+ return true;
+ }
+ if ( firstNonWeakImage == NULL ) {
+ firstNonWeakImage = foundInImage;
+ firstNonWeakSym = *sym;
+ }
}
}
}
}
+ if ( firstNonWeakImage != NULL ) {
+ // found a weak definition, but no non-weak, so return first weak found
+ *sym = firstNonWeakSym;
+ *image = firstNonWeakImage;
+ return true;
+ }
if ( firstWeakSym != NULL ) {
// found a weak definition, but no non-weak, so return first weak found
*sym = firstWeakSym;
}
#if SUPPORT_ACCELERATE_TABLES
if ( sAllCacheImagesProxy != NULL ) {
- if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) )
+ if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image, notifier) )
return true;
}
#endif
return findExportedSymbol(name, false, sym, image);
}
-bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image)
+bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier)
{
- return findExportedSymbol(name, true, sym, image);
+ return findExportedSymbol(name, true, sym, image, notifier);
}
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
#endif
-static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex)
+static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex)
{
dyld::LoadContext context;
context.useSearchPaths = search;
context.mustBeBundle = false;
context.mustBeDylib = true;
context.canBePIE = false;
+ context.enforceIOSMac = enforceIOSMac;
context.origin = origin;
context.rpath = rpaths;
return load(libraryName, context, cacheIndex);
}
// collect phase: run termination routines for images not marked in-use
- __cxa_range_t ranges[maxRangeCount];
- int rangeCount = 0;
- for (unsigned i=0; i < deadCount; ++i) {
- ImageLoader* image = deadImages[i];
- for (unsigned int j=0; j < image->segmentCount(); ++j) {
- if ( !image->segExecutable(j) )
- continue;
- if ( rangeCount < maxRangeCount ) {
- ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j);
- ranges[rangeCount].length = image->segSize(j);
- ++rangeCount;
+ if ( maxRangeCount != 0 ) {
+ __cxa_range_t ranges[maxRangeCount];
+ int rangeCount = 0;
+ for (unsigned i=0; i < deadCount; ++i) {
+ ImageLoader* image = deadImages[i];
+ for (unsigned int j=0; j < image->segmentCount(); ++j) {
+ if ( !image->segExecutable(j) )
+ continue;
+ if ( rangeCount < maxRangeCount ) {
+ ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j);
+ ranges[rangeCount].length = image->segSize(j);
+ ++rangeCount;
+ }
+ }
+ try {
+ runImageStaticTerminators(image);
+ }
+ catch (const char* msg) {
+ dyld::warn("problem running terminators for image: %s\n", msg);
}
}
- try {
- runImageStaticTerminators(image);
- }
- catch (const char* msg) {
- dyld::warn("problem running terminators for image: %s\n", msg);
- }
+
+ // <rdar://problem/14718598> dyld should call __cxa_finalize_ranges()
+ if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) )
+ (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount);
}
-
- // <rdar://problem/14718598> dyld should call __cxa_finalize_ranges()
- if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) )
- (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount);
// collect phase: delete all images which are not marked in-use
bool mightBeMore;
context.mustBeBundle = false;
context.mustBeDylib = true;
context.canBePIE = false;
+ context.enforceIOSMac = true;
context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES
context.rpath = NULL;
image = load(path, context, cacheIndex);
}
catch (const char* msg) {
-#if TARGET_IPHONE_SIMULATOR
- dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg);
-#else
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processUsingLibraryValidation )
- dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg);
+ if ( gLinkContext.allowInsertFailures )
+ dyld::log("dyld: warning: could not load inserted library '%s' into hardened process because %s\n", path, msg);
else
-#endif
halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg));
-#endif
}
catch (...) {
halt(dyld::mkstringf("could not load inserted library '%s'\n", path));
}
-//
-// Sets:
-// sEnvMode
-// gLinkContext.requireCodeSignature
-// gLinkContext.processIsRestricted // Mac OS X only
-// gLinkContext.processUsingLibraryValidation // Mac OS X only
-//
static void configureProcessRestrictions(const macho_header* mainExecutableMH)
{
+ uint64_t amfiInputFlags = 0;
#if TARGET_IPHONE_SIMULATOR
- sEnvMode = envAll;
- gLinkContext.requireCodeSignature = true;
+ amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR;
+#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( hasRestrictedSegment(mainExecutableMH) )
+ amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG;
#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 ) {
- // Xcode built app for Debug allowed to use DYLD_* variables
- sEnvMode = envAll;
+ if ( isFairPlayEncrypted(mainExecutableMH) )
+ amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED;
+#endif
+ uint64_t amfiOutputFlags = 0;
+ if ( amfi_check_dyld_policy_self(amfiInputFlags, &amfiOutputFlags) == 0 ) {
+ gLinkContext.allowAtPaths = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_AT_PATH);
+ gLinkContext.allowEnvVarsPrint = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS);
+ gLinkContext.allowEnvVarsPath = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS);
+ gLinkContext.allowEnvVarsSharedCache = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE);
+ gLinkContext.allowClassicFallbackPaths = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS);
+ gLinkContext.allowInsertFailures = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION);
+ }
+ else {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // support chrooting from old kernel
+ bool isRestricted = false;
+ bool libraryValidation = false;
+ // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
+ if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
+ isRestricted = 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 ) {
+ isRestricted = true;
}
- else {
- // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app
- uint32_t secureValue = 0;
- size_t secureValueSize = sizeof(secureValue);
- if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) {
- sEnvMode = envPrintOnly;
- }
+ // Library Validation loosens searching but requires everything to be code signed
+ if ( flags & CS_REQUIRE_LV ) {
+ isRestricted = false;
+ libraryValidation = true;
}
}
- else {
- // Development kernel can run unsigned code
- sEnvMode = envAll;
- gLinkContext.requireCodeSignature = false;
- }
- }
- if ( issetugid() ) {
- sEnvMode = envNone;
- }
-#elif __MAC_OS_X_VERSION_MIN_REQUIRED
- sEnvMode = envAll;
- gLinkContext.requireCodeSignature = false;
- gLinkContext.processIsRestricted = false;
- gLinkContext.processUsingLibraryValidation = false;
- // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
- if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
- gLinkContext.processIsRestricted = true;
- }
- 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 ) {
- gLinkContext.processIsRestricted = true;
- }
- // Library Validation loosens searching but requires everything to be code signed
- if ( flags & CS_REQUIRE_LV ) {
- gLinkContext.processIsRestricted = false;
- //gLinkContext.requireCodeSignature = true;
- gLinkContext.processUsingLibraryValidation = true;
- sSafeMode = usingSIP;
- }
- }
+ gLinkContext.allowAtPaths = !isRestricted;
+ gLinkContext.allowEnvVarsPrint = !isRestricted;
+ gLinkContext.allowEnvVarsPath = !isRestricted;
+ gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP;
+ gLinkContext.allowClassicFallbackPaths = !isRestricted;
+ gLinkContext.allowInsertFailures = false;
+#else
+ halt("amfi_check_dyld_policy_self() failed\n");
#endif
+ }
}
bool processIsRestricted()
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- return gLinkContext.processIsRestricted;
+ return !gLinkContext.allowEnvVarsPath;
#else
return false;
#endif
typedef int (*ioctl_proc_t)(int, unsigned long, void*);
static void* getProcessInfo() { return dyld::gProcessInfo; }
static SyscallHelpers sSysCalls = {
- 8,
+ 12,
// added in version 1
(open_proc_t)&open,
&close,
&task_info,
&thread_info,
&kdebug_is_enabled,
- &kdebug_trace
+ &kdebug_trace,
+ // Added in version 9
+ &kdebug_trace_string,
+ // Added in version 10
+ &amfi_check_dyld_policy_self,
+ // Added in version 11
+ ¬ifyMonitoringDyldMain,
+ ¬ifyMonitoringDyld,
+ // Add in version 12
+ &mach_msg_destroy,
+ &mach_port_construct,
+ &mach_port_destruct
};
__attribute__((noinline))
*startGlue = 0;
*mainAddr = 0;
+ // <rdar://35873436> HACK to allow marzipan dyld_sim to run entitled processes
+ if ( strncmp(dyldPath, "/System/", 8) != 0 ) {
+ uint32_t flags;
+ if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 )
+ return "csops() failed";
+ if ( (flags & CS_RESTRICT) == CS_RESTRICT )
+ return "dyld_sim cannot be loaded in a restricted process";
+ }
+
// <rdar://problem/25311921> simulator does not support restricted processes
- uint32_t flags;
- if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 )
- return "csops() failed";
- if ( (flags & CS_RESTRICT) == CS_RESTRICT )
- return "dyld_sim cannot be loaded in a restricted process";
if ( issetugid() )
return "dyld_sim cannot be loaded in a setuid process";
if ( hasRestrictedSegment(mainExecutableMH) )
return "dyld_sim load commands to large";
if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 )
return "dyld_sim load commands to large";
+ struct linkedit_data_command* codeSigCmd = NULL;
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds);
const struct load_command* cmd = cmds;
break;
case LC_SEGMENT_COMMAND_WRONG:
return "dyld_sim wrong load segment load command";
+ case LC_CODE_SIGNATURE:
+ codeSigCmd = (struct linkedit_data_command*)cmd;
+ break;
}
cmd = nextCmd;
}
// last segment must be named __LINKEDIT and not writable
+ if ( lastSeg == NULL )
+ return "dyld_sim has no segments";
if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 )
return "dyld_sim last segment not __LINKEDIT";
if ( lastSeg->initprot & VM_PROT_WRITE )
return "dyld_sim __LINKEDIT segment writable";
+ // must have code signature which is contained within LINKEDIT segment
+ if ( codeSigCmd == NULL )
+ return "dyld_sim not code signed";
+ if ( codeSigCmd->dataoff < lastSeg->fileoff )
+ return "dyld_sim code signature not in __LINKEDIT";
+ if ( (codeSigCmd->dataoff + codeSigCmd->datasize) < codeSigCmd->dataoff )
+ return "dyld_sim code signature size wraps";
+ if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
+ return "dyld_sim code signature extends beyond __LINKEDIT";
+
+ // register code signature with kernel before mmap()ing segments
+ fsignatures_t siginfo;
+ siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file
+ siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of code-signature in mach-o file
+ siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature
+ int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo);
+ if ( result == -1 ) {
+ return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno);
+ }
+ // file range covered by code signature must extend up to code signature itself
+ 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);
+
// reserve space, then mmap each segment
vm_address_t loadAddress = 0;
if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 )
return "dyld_sim cannot allocate space";
cmd = cmds;
- struct linkedit_data_command* codeSigCmd = NULL;
struct source_version_command* dyldVersionCmd = NULL;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
return "dyld_sim mmap() to wrong location";
}
break;
- case LC_CODE_SIGNATURE:
- codeSigCmd = (struct linkedit_data_command*)cmd;
- break;
case LC_SOURCE_VERSION:
dyldVersionCmd = (struct source_version_command*)cmd;
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
-
- // must have code signature which is contained within LINKEDIT segment
- if ( codeSigCmd == NULL )
- return "dyld_sim not code signed";
- if ( codeSigCmd->dataoff < lastSeg->fileoff )
- return "dyld_sim code signature not in __LINKEDIT";
- if ( (codeSigCmd->dataoff + codeSigCmd->datasize) < codeSigCmd->dataoff )
- return "dyld_sim code signature size wraps";
- if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
- return "dyld_sim code signature extends beyond __LINKEDIT";
-
- fsignatures_t siginfo;
- siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file
- siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of code-signature in mach-o file
- siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature
- int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo);
- if ( result == -1 ) {
- return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno);
- }
close(fd);
- // file range covered by code signature must extend up to code signature itself
- if ( siginfo.fs_file_start < codeSigCmd->dataoff )
- return 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 = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
+ if ( entry == 0 )
+ return "dyld_sim entry not found";
// notify debugger that dyld_sim is loaded
dyld_image_info info;
-static bool envVarMatches(dyld3::launch_cache::Closure mainClosure, const char* envp[], const char* varName)
+static bool envVarMatches(const dyld3::closure::LaunchClosure* mainClosure, const char* envp[], const char* varName)
{
__block const char* valueFromClosure = nullptr;
- mainClosure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+ mainClosure->forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
size_t keyLen = strlen(varName);
if ( (strncmp(varName, keyEqualValue, keyLen) == 0) && (keyEqualValue[keyLen] == '=') ) {
valueFromClosure = &keyEqualValue[keyLen+1];
"DYLD_ROOT_PATH"
};
-static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* envp[])
+static bool envVarsMatch(const dyld3::closure::LaunchClosure* 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);
+ dyld::log("dyld: closure %p not used because %s changed\n", mainClosure, envVar);
return false;
}
}
// <rdar://problem/37004660> dyld3: support DYLD_VERSIONED_*_PATHs ?
if ( sEnv.DYLD_VERSIONED_LIBRARY_PATH != nullptr ) {
if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p not used because DYLD_VERSIONED_LIBRARY_PATH used\n", mainClosure.binaryData());
+ dyld::log("dyld: closure %p not used because DYLD_VERSIONED_LIBRARY_PATH used\n", mainClosure);
return false;
}
if ( sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != nullptr ) {
if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p not used because DYLD_VERSIONED_FRAMEWORK_PATH used\n", mainClosure.binaryData());
+ dyld::log("dyld: closure %p not used because DYLD_VERSIONED_FRAMEWORK_PATH used\n", mainClosure);
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[])
+static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const dyld3::closure::LoadedFileInfo& mainFileInfo,
+ 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;
+ // verify current dyld cache is same as expected
+ uuid_t expectedCacheUUID;
+ if ( mainClosure->builtAgainstDyldCache(expectedCacheUUID) ) {
+ if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosure);
+ return false;
+ }
+ else {
+ uuid_t actualCacheUUID;
+ sSharedCacheLoadInfo.loadAddress->getUUID(actualCacheUUID);
+ if ( memcmp(expectedCacheUUID, actualCacheUUID, sizeof(uuid_t)) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosure);
+ return false;
+ }
+ }
}
- }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- else {
- // If the in-memory cache doesn't have the same UUID xattr as the on-disk cache then we must
- // have built a new cache but not rebooted. In this case, don't use dyld3.
- const char* sharedCachePath = getStandardSharedCacheFilePath();
- uuid_t inMemoryUUID;
- uuid_t onDiskUUID;
- sharedCacheUUID(inMemoryUUID);
- if (getxattr(sharedCachePath, "cacheUUID", (void*)&onDiskUUID, sizeof(uuid_t), 0, 0) != sizeof(uuid_t)) {
- if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p on disk cache doesn't have a UUID xattr\n", mainClosureData);
- return false;
+ else {
+ // closure built assume there is no dyld cache
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p built expecting no dyld cache\n", mainClosure);
+ return false;
+ }
}
- if (memcmp(&inMemoryUUID, &onDiskUUID, sizeof(uuid_t)) != 0) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // verify this closure is not from a previous reboot
+ const char* expectedBootUUID = mainClosure->bootUUID();
+ char actualBootSessionUUID[256] = { 0 };
+ size_t bootSize = sizeof(actualBootSessionUUID);
+ bool gotActualBootUUID = (sysctlbyname("kern.bootsessionuuid", actualBootSessionUUID, &bootSize, NULL, 0) == 0);
+ if ( !gotActualBootUUID || (expectedBootUUID == nullptr) || (strcmp(expectedBootUUID, actualBootSessionUUID) != 0) ) {
if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p not used because current cache on disk and in memory cache have UUID mismatches\n", mainClosureData);
+ dyld::log("dyld: closure %p built in different boot context\n", mainClosure);
return false;
}
- }
#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 all mach-o files have not changed since closure was built
+ __block bool foundFileThatInvalidatesClosure = false;
+ mainClosure->images()->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ __block uint64_t expectedInode;
+ __block uint64_t expectedMtime;
+ if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
+ struct stat statBuf;
+ if ( ::stat(image->path(), &statBuf) == 0 ) {
+ if ( (statBuf.st_mtime != expectedMtime) || (statBuf.st_ino != expectedInode) ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure, image->path());
+ foundFileThatInvalidatesClosure = true;
+ stop = true;
+ }
+ }
+ else {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because '%s' is needed by closure but is missing\n", mainClosure, image->path());
+ foundFileThatInvalidatesClosure = true;
+ stop = true;
+ }
+ }
+ });
+ if ( foundFileThatInvalidatesClosure )
+ return false;
// verify cdHash of main executable is same as recorded in closure
- if ( mainImage.validateUsingCdHash() ) {
+ uint8_t expectedHash[20];
+ const dyld3::closure::Image* mainImage = mainClosure->images()->imageForNum(mainClosure->topImage());
+ if ( mainImage->hasCdHash(expectedHash) ) {
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);
+ dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosure);
return false;
}
- if ( memcmp(mainExecutableCDHash, mainClosure.cdHash(), 20) != 0 ) {
+ if ( memcmp(mainExecutableCDHash, expectedHash, 20) != 0 ) {
if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosureData);
+ dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosure);
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 expectedUUID;
+ bool hasExpect = mainImage->getUuid(expectedUUID);
uuid_t actualUUID;
- parser.getUuid(actualUUID);
- if ( memcmp(actualUUID, closureMainUUID, sizeof(uuid_t)) != 0 ) {
+ const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent;
+ bool hasActual = mainExecutableMH->getUuid(actualUUID);
+ if ( hasExpect != hasActual ) {
if ( gLinkContext.verboseWarnings )
- dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosureData);
+ dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure);
+ return false;
+ }
+ if ( hasExpect && hasActual && memcmp(actualUUID, expectedUUID, sizeof(uuid_t)) != 0 ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure);
return false;
}
}
// verify files that are supposed to be missing actually are missing
- __block bool foundFileThatInvalidatesClosure = false;
- mainClosure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+ 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);
+ dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosure, 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;
- }
- }
- }
- });
+ // verify closure did not require anything unavailable
+ if ( mainClosure->usedAtPaths() && !gLinkContext.allowAtPaths ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because is used @paths, but process does not allow that\n", mainClosure);
+ return false;
+ }
+ if ( mainClosure->usedFallbackPaths() && !gLinkContext.allowClassicFallbackPaths ) {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: closure %p not used because is used default fallback paths, but process does not allow that\n", mainClosure);
+ return false;
}
-#endif
return !foundFileThatInvalidatesClosure;
}
return true;
}
-static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* mainClosureData,
+static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
const DyldSharedCache* dyldCache,
- const mach_header* mainExecutableMH, uintptr_t mainExecutableSlide,
+ const dyld3::MachOLoaded* 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;
+ // build list of all known ImageArrays (at most three: cached dylibs, other OS dylibs, and main prog)
+ STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray*, imagesArrays, 3);
+ const dyld3::closure::ImageArray* mainClosureImages = mainClosure->images();
+ if ( dyldCache != nullptr ) {
+ imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+ if ( auto others = dyldCache->otherOSImageArray() )
+ imagesArrays.push_back(others);
+ }
+ imagesArrays.push_back(mainClosureImages);
+
+ // allocate space for Array<LoadedImage>
+ STACK_ALLOC_ARRAY(dyld3::LoadedImage, allImages, mainClosure->initialLoadCount());
+
+ __block dyld3::Loader loader(allImages, dyldCache, imagesArrays, (gLinkContext.verboseLoading ? &dolog : &nolog),
+ (gLinkContext.verboseMapping ? &dolog : &nolog),
+ (gLinkContext.verboseBind ? &dolog : &nolog),
+ (gLinkContext.verboseDOF ? &dolog : &nolog));
+ dyld3::closure::ImageNum mainImageNum = mainClosure->topImage();
+ mainClosureImages->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ if ( image->imageNum() == mainImageNum ) {
+ // add main executable (which is already mapped by kernel) to list
+ dyld3::LoadedImage mainLoadedImage = dyld3::LoadedImage::make(image, mainExecutableMH);
+ mainLoadedImage.setState(dyld3::LoadedImage::State::mapped);
+ mainLoadedImage.markLeaveMapped();
+ loader.addImage(mainLoadedImage);
+ stop = true;
}
- }
- 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
+ else {
+ // add inserted library to initial list
+ loader.addImage(dyld3::LoadedImage::make(image));
+ }
+ });
+
+ // recursively load all dependents and fill in allImages array
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));
+ loader.completeAllDependents(diag);
+ if ( diag.noError() )
+ loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled());
if ( diag.hasError() ) {
if ( gLinkContext.verboseWarnings )
dyld::log("dyld: %s\n", diag.errorMessage());
}
//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());
+ //for (const dyld3::LoadedImage& info : allImages) {
+ // dyld::log("mh=%p, path=%s\n", info.loadedAddress(), info.image()->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;
- }
+ // find libdyld entry
+ dyld3::closure::Image::ResolvedSymbolTarget dyldEntry;
+ mainClosure->libDyldEntry(dyldEntry);
+ const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)loader.resolveTarget(dyldEntry);
// 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);
+ if ( libDyldEntry->vectorVersion > 4 )
+ libDyldEntry->setRestrictions(gLinkContext.allowAtPaths, gLinkContext.allowEnvVarsPath);
libDyldEntry->setHaltFunction(&halt);
+ if ( libDyldEntry->vectorVersion > 5 ) {
+ libDyldEntry->setNotifyMonitoringDyldMain(¬ifyMonitoringDyldMain);
+ libDyldEntry->setNotifyMonitoringDyld(¬ifyMonitoringDyld);
+ }
if ( libDyldEntry->vectorVersion > 2 )
libDyldEntry->setChildForkFunction(&_dyld_fork_child);
#if !TARGET_IPHONE_SIMULATOR
libDyldEntry->setLogFunction(&dyld::vlog);
#endif
libDyldEntry->setOldAllImageInfo(gProcessInfo);
- libDyldEntry->setInitialImageList(mainClosureData, dyldCache, sSharedCacheLoadInfo.path, allImages, libSystemMH, libSystemImage);
+ const dyld3::LoadedImage* libSys = loader.findImage(mainClosure->libSystemImageNum());
+ libDyldEntry->setInitialImageList(mainClosure, dyldCache, sSharedCacheLoadInfo.path, allImages, *libSys);
// 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();
+ if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+ dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 3);
}
- else {
+ dyld3::closure::Image::ResolvedSymbolTarget progEntry;
+ if ( mainClosure->mainEntry(progEntry) ) {
// 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();
+ *entry = loader.resolveTarget(progEntry);
}
- CRSetCrashLogMessage(NULL);
+ else if ( mainClosure->startEntry(progEntry) ) {
+ // old style app linked with crt1.o
+ // entry is "start" function in program
+ *startGlue = 0;
+ *entry = loader.resolveTarget(progEntry);
+ }
+ else {
+ assert(0);
+ }
+
+ CRSetCrashLogMessage("dyld3 mode");
return true;
}
+
+#if !TARGET_IPHONE_SIMULATOR
+
static void putHexNibble(uint8_t value, char*& p)
{
if ( value < 10 )
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);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static void makeHashOfProgramAndEnv(const char* mainExecutablePath, const uint8_t* mainExecutableCDHash, const char* envp[], uint8_t hash32[32])
+{
+ // create hash of main path, main cd hash, cache UUID, DYLD_* env vars
+ const struct ccdigest_info* di = ccsha256_di();
+ ccdigest_di_decl(di, hashTemp); // defines hashTemp array in stack
+ ccdigest_init(di, hashTemp);
+ // hash in main executable path
+ ccdigest_update(di, hashTemp, strlen(mainExecutablePath), mainExecutablePath);
+ // hash in cdHash of main executable
+ if ( mainExecutableCDHash != nullptr )
+ ccdigest_update(di, hashTemp, 20, mainExecutableCDHash);
+ // hash in shared cache UUID
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ uuid_t cacheUUID;
+ sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+ ccdigest_update(di, hashTemp, sizeof(uuid_t), cacheUUID);
+ }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // hash in if process is restricted
+ ccdigest_update(di, hashTemp, sizeof(gLinkContext.allowEnvVarsPath), &gLinkContext.allowEnvVarsPath);
#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';
+ // include dyld's UUID so changing dyld invalidates closures
+ uuid_t dyldUUID;
+ if ( ((const dyld3::MachOLoaded*)&__dso_handle)->getUuid(dyldUUID) )
+ ccdigest_update(di, hashTemp, sizeof(uuid_t), dyldUUID);
+
+ // hash in DYLD_* env vars
+ for (const char* envVar : sEnvVarsToCheck) {
+ if ( const char* keyValue = _simple_getenv(envp, envVar) )
+ ccdigest_update(di, hashTemp, strlen(keyValue), keyValue);
+ }
+ // finish SHA256 into 32-byte value
+ ccdigest_final(di, hashTemp, hash32);
+ ccdigest_di_clear(di, hashTemp);
}
+#endif
-#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;
+static void buildClosureCachePath(const char* mainExecutablePath,const dyld3::MachOLoaded* mainExecutableMH,
+ const uint8_t* mainExecutableCDHash, const char* envp[], char closurePath[])
+{
+ // build base path of $TMPDIR/dyld/<prog-name>-
+ const char* tempDir = _simple_getenv(envp, "TMPDIR");
+ if ( tempDir == nullptr )
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ tempDir = "/private/tmp/";
+#else
+ tempDir = "/private/var/tmp/";
+#endif
+ strlcpy(closurePath, tempDir, PATH_MAX);
+ strlcat(closurePath, "/com.apple.dyld/", PATH_MAX);
+
+ // make sure dyld sub-dir exists
+ struct stat statbuf;
+ if ( ::stat(closurePath, &statbuf) != 0 ) {
+ ::mkdir(closurePath, S_IRWXU);
+ }
+ const char* leafName = strrchr(mainExecutablePath, '/');
+ if ( leafName == nullptr )
+ leafName = mainExecutablePath;
+ else
+ ++leafName;
+ strlcat(closurePath, leafName, PATH_MAX);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // on macOS we allow multiple closures by hashing the env vars into the cache filename
+ strlcat(closurePath, "-", PATH_MAX);
+ uint8_t hash32[32];
+ makeHashOfProgramAndEnv(mainExecutablePath, mainExecutableCDHash, envp, hash32);
+ char hashString[72];
+ char* s = hashString;
+ for (int i=0; i < 32; ++i)
+ putHexByte(hash32[i], s);
+ *s = '\0';
+ strlcat(closurePath, hashString, PATH_MAX);
#else
- // get port to closured
- mach_port_t serverPort = dyld3::loader::lookupClosuredPort();
- if ( serverPort == MACH_PORT_NULL )
- return NULL;
+ // on iOS, the file name is the leaf name and UUID
+ uuid_t mainExeUUID;
+ if ( mainExecutableMH->getUuid(mainExeUUID) ) {
+ char mainUuidStr[40];
+ bytesToHex(mainExeUUID, sizeof(uuid_t), mainUuidStr);
+ strlcat(closurePath, "-", PATH_MAX);
+ strlcat(closurePath, mainUuidStr, PATH_MAX);
+ }
+#endif
+ strlcat(closurePath, ".closure", PATH_MAX);
+}
- // 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;
- }
+static const dyld3::closure::LaunchClosure* mapClosureFile(const char* closurePath)
+{
+ struct stat statbuf;
+ if ( ::stat(closurePath, &statbuf) == -1 )
+ return nullptr;
+
+ int fd = ::open(closurePath, O_RDONLY);
+ if ( fd < 0 )
+ return nullptr;
+
+ const dyld3::closure::LaunchClosure* closure = (dyld3::closure::LaunchClosure*)::mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ ::close(fd);
+
+ if ( closure == MAP_FAILED )
+ return nullptr;
+
+ return closure;
+}
+
+static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* mainExecutableCDHash,
+ const dyld3::closure::LoadedFileInfo& mainFileInfo, const char* envp[])
+{
+ const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent;
+ dyld3::closure::PathOverrides pathOverrides;
+ pathOverrides.setFallbackPathHandling(gLinkContext.allowClassicFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::restricted);
+ pathOverrides.setEnvVars(envp, mainExecutableMH, mainFileInfo.path);
+ STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray*, imagesArrays, 3);
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ imagesArrays.push_back(sSharedCacheLoadInfo.loadAddress->cachedDylibsImageArray());
+ if ( auto others = sSharedCacheLoadInfo.loadAddress->otherOSImageArray() )
+ imagesArrays.push_back(others);
}
- *s++ = '\0';
- // get uuid of main executable
- dyld3::MachOParser mainParser((mach_header*)sMainExecutableMachHeader);
- uuid_t mainUuid;
- mainParser.getUuid(mainUuid);
+ dyld3::closure::ClosureBuilder::LaunchErrorInfo* errorInfo = (dyld3::closure::ClosureBuilder::LaunchErrorInfo*)&gProcessInfo->errorKind;
+ dyld3::closure::FileSystemPhysical fileSystem;
+ dyld3::closure::ClosureBuilder::AtPath atPathHanding = (gLinkContext.allowAtPaths ? dyld3::closure::ClosureBuilder::AtPath::all : dyld3::closure::ClosureBuilder::AtPath::none);
+ dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, sSharedCacheLoadInfo.loadAddress, true, pathOverrides, atPathHanding, errorInfo, mainExecutableMH->archName());
+ const dyld3::closure::LaunchClosure* result = builder.makeLaunchClosure(mainFileInfo, gLinkContext.allowInsertFailures);
+ if ( builder.diagnostics().hasError() )
+ halt(builder.diagnostics().errorMessage());
- // 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;
+ if ( result == nullptr )
+ return nullptr;
- // release server port
- mach_port_deallocate(mach_task_self(), serverPort);
+ if ( !closureValid(result, mainFileInfo, mainExecutableCDHash, false, envp) ) {
+ // some how the freshly generated closure is invalid...
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: somehow just built closure is invalid\n");
+ return nullptr;
+ }
+ // try to save closure to disk for next launch (atomically)
+ char closurePath[PATH_MAX];
+ buildClosureCachePath(mainFileInfo.path, mainExecutableMH, mainExecutableCDHash, envp, closurePath);
+ char closurePathTemp[PATH_MAX];
+ strlcpy(closurePathTemp, closurePath, PATH_MAX);
+ int mypid = getpid();
+ char pidBuf[16];
+ char* s = pidBuf;
+ *s++ = '.';
+ putHexByte(mypid >> 24, s);
+ putHexByte(mypid >> 16, s);
+ putHexByte(mypid >> 8, s);
+ putHexByte(mypid, s);
+ *s = '\0';
+ strlcat(closurePathTemp, pidBuf, PATH_MAX);
+ int fd = ::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
+ if ( fd != -1 ) {
+ ::ftruncate(fd, result->size());
+ ::write(fd, result, result->size());
+ ::fchmod(fd, S_IRUSR);
+ ::close(fd);
+ ::rename(closurePathTemp, closurePath);
+ // free built closure and mmap file() to reduce dirty memory
+ result->deallocate();
+ result = mapClosureFile(closurePath);
+ }
+ else if ( gLinkContext.verboseWarnings ) {
+ dyld::log("could not save closure (errno=%d) to: %s\n", errno, closurePathTemp);
+ }
+
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: just built closure %p (size=%lu) for %s\n", result, result->size(), sExecPath);
- if ( success )
- return (const dyld3::launch_cache::BinaryClosureData*)reply;
+ return result;
+}
- dyld::log("closure failed to build: %s\n", (char*)reply);
- return NULL;
-#endif
+static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_t* mainExecutableCDHash,
+ const dyld3::closure::LoadedFileInfo& mainFileInfo,
+ const char* envp[])
+{
+ char closurePath[PATH_MAX];
+ buildClosureCachePath(mainFileInfo.path, (const dyld3::MachOLoaded*)mainFileInfo.fileContent, mainExecutableCDHash, envp, closurePath);
+ const dyld3::closure::LaunchClosure* closure = mapClosureFile(closurePath);
+ if ( closure == nullptr )
+ return nullptr;
+
+ if ( !closureValid(closure, mainFileInfo, mainExecutableCDHash, false, envp) ) {
+ ::munmap((void*)closure, closure->size());
+ return nullptr;
+ }
+
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: used cached closure %p (size=%lu) for %s\n", closure, closure->size(), sExecPath);
+
+ return closure;
}
+
#endif // !TARGET_IPHONE_SIMULATOR
+
#if !__MAC_OS_X_VERSION_MIN_REQUIRED
static const char* sWhiteListDirs[] = {
"/bin/",
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") )
+ if ( dyld3::bootArgsContains("force_dyld2=1") )
return false;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
#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 dyld3::bootArgsContains("force_dyld3=1");
+#endif
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static bool isStagedApp(const dyld3::MachOFile* mainExecutableMH, const char* mainExecutablePath)
+{
+#if !__MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( (strncmp(mainExecutablePath, "/var/containers/Bundle/Application/", 35) == 0)
+ || (strncmp(mainExecutablePath, "/private/var/containers/Bundle/Application/", 43) == 0) ) {
+ // staged apps are built without LC_ENCRYPTION_INFO
+ if ( !mainExecutableMH->canBeFairPlayEncrypted() )
return true;
- }
}
- return dyld3::loader::bootArgsContains("force_dyld3=1");
#endif
+ return false;
}
+#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);
+ if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+ launchTraceID = dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, (uint64_t)mainExecutableMH, 0, 0);
+ }
- // Grab the cdHash of the main executable from the environment
+ // 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) )
#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 ) {
-
+ if ( (rootPath != NULL) ) {
// look to see if simulator has its own dyld
char simDyldPath[PATH_MAX];
strlcpy(simDyldPath, rootPath, PATH_MAX);
configureProcessRestrictions(mainExecutableMH);
#if __MAC_OS_X_VERSION_MIN_REQUIRED
- if ( gLinkContext.processIsRestricted ) {
+ if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
pruneEnvironmentVariables(envp, &apple);
// set again because envp and apple may have changed or moved
setContext(mainExecutableMH, argc, argv, envp, apple);
checkEnvironmentVariables(envp);
defaultUninitializedFallbackPaths(envp);
}
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( ((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::iOSMac)
+ && !((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::macOS)) {
+ gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL);
+ gLinkContext.marzipan = true;
+ if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == sLibraryFallbackPaths )
+ sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths;
+ if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == sFrameworkFallbackPaths )
+ sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
+ }
+#endif
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
getHostInfo(mainExecutableMH, mainExecutableSlide);
// load shared cache
- checkSharedRegionDisable((mach_header*)mainExecutableMH);
+ checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
#if TARGET_IPHONE_SIMULATOR
// <HACK> until <rdar://30773711> is fixed
gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
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);
- }
- }
- }
+ bool cacheCompatible = (sSharedCacheLoadInfo.loadAddress == nullptr) || (sSharedCacheLoadInfo.loadAddress->header.formatVersion == dyld3::closure::kFormatVersion);
+ if ( cacheCompatible && (sEnableClosures || inWhiteList(sExecPath)) ) {
+ const dyld3::closure::LaunchClosure* mainClosure = nullptr;
+ dyld3::closure::LoadedFileInfo mainFileInfo;
+ mainFileInfo.fileContent = mainExecutableMH;
+ mainFileInfo.path = sExecPath;
+ // FIXME: If we are saving this closure, this slice offset/length is probably wrong in the case of FAT files.
+ mainFileInfo.sliceOffset = 0;
+ mainFileInfo.sliceLen = std::numeric_limits<__typeof(mainFileInfo.sliceLen)>::max();
+ struct stat mainExeStatBuf;
+ if ( ::stat(sExecPath, &mainExeStatBuf) == 0 ) {
+ mainFileInfo.inode = mainExeStatBuf.st_ino;
+ mainFileInfo.mtime = mainExeStatBuf.st_mtime;
+ }
+ // check for closure in cache first
+ if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+ mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
+ if ( gLinkContext.verboseWarnings && (mainClosure != nullptr) )
+ dyld::log("dyld: found closure %p (size=%lu) in dyld shared cache\n", mainClosure, mainClosure->size());
+ }
+ #if !TARGET_IPHONE_SIMULATOR
+ if ( (mainClosure == nullptr) || !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) {
+ mainClosure = nullptr;
+ if ( sEnableClosures || isStagedApp((dyld3::MachOFile*)mainExecutableMH, sExecPath) ) {
+ // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
+ mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+ if ( mainClosure == nullptr ) {
+ // if no cached closure found, build new one
+ mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
}
}
+ }
#endif
- if ( gLinkContext.verboseWarnings && (mainClosureData != nullptr) )
- dyld::log("dyld: found closure %p in dyld shared cache\n", mainClosureData);
+ // try using launch closure
+ if ( mainClosure != nullptr ) {
+ CRSetCrashLogMessage("dyld3: launch started");
+ bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
+ mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
#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;
- }
+ if ( !launched ) {
+ // closure is out of date, build new one
+ mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+ if ( mainClosure != nullptr ) {
+ launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
+ mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
}
}
#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);
- }
+ if ( launched ) {
+#if __has_feature(ptrauth_calls)
+ // start() calls the result pointer as a function pointer so we need to sign it.
+ result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
+#endif
+ if (sSkipMain)
+ result = (uintptr_t)&fake_main;
+ return result;
+ }
+ else {
+ if ( gLinkContext.verboseWarnings )
+ dyld::log("dyld: unable to use closure %p\n", mainClosure);
}
}
- 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
}
+ 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
sImageRoots.reserve(16);
sAddImageCallbacks.reserve(4);
sRemoveImageCallbacks.reserve(4);
+ sAddLoadImageCallbacks.reserve(4);
sImageFilesNeedingTermination.reserve(16);
sImageFilesNeedingDOFUnregistration.reserve(8);
addDyldImageToUUIDList();
#if SUPPORT_ACCELERATE_TABLES
+#if __arm64e__
+ // Disable accelerator tables when we have threaded rebase/bind, which is arm64e executables only for now.
+ if (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64_E)
+ sDisableAcceleratorTables = true;
+#endif
bool mainExcutableAlreadyRebased = false;
if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
struct stat statBuf;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// <rdar://problem/22805519> be less strict about old mach-o binaries
uint32_t mainSDK = sMainExecutable->sdkVersion();
- gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation;
+ gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.allowInsertFailures;
#else
// simulators, iOS, tvOS, and watchOS are always strict
gLinkContext.strictMachORequired = true;
// register interposing info after all inserted libraries are bound so chaining works
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
- image->registerInterposing();
+ image->registerInterposing(gLinkContext);
}
}
ImageLoader* image = sAllImages[i];
if ( image->inSharedCache() )
continue;
- image->registerInterposing();
+ image->registerInterposing(gLinkContext);
}
#if SUPPORT_ACCELERATE_TABLES
if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
sImageFilesNeedingDOFUnregistration.clear();
sAddImageCallbacks.clear();
sRemoveImageCallbacks.clear();
+ sAddLoadImageCallbacks.clear();
sDisableAcceleratorTables = true;
sAllCacheImagesProxy = NULL;
sMappedRangesStart = NULL;
for(int i=0; i < sImageRoots.size(); ++i) {
sImageRoots[i]->applyInterposing(gLinkContext);
}
+ ImageLoader::applyInterposingToDyldCache(gLinkContext);
gLinkContext.linkingMainExecutable = false;
+
+ // Bind and notify for the main executable now that interposing has been registered
+ uint64_t bindMainExecutableStartTime = mach_absolute_time();
+ sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
+ uint64_t bindMainExecutableEndTime = mach_absolute_time();
+ ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
+ gLinkContext.notifyBatch(dyld_image_state_bound, false);
+
+ // Bind and notify for the inserted images now interposing has been registered
+ if ( sInsertedDylibCount > 0 ) {
+ for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
+ ImageLoader* image = sAllImages[i+1];
+ image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
+ }
+ }
// <rdar://problem/12186933> do weak binding only after all inserted images linked
sMainExecutable->weakBind(gLinkContext);
#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);
+ if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+ dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
+ }
notifyMonitoringDyldMain();
// find entry point for main executable
- result = (uintptr_t)sMainExecutable->getThreadPC();
+ result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
- // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib
+ // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
}
else {
// main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
- result = (uintptr_t)sMainExecutable->getMain();
+ result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
+#if __has_feature(ptrauth_calls)
+ // start() calls the result pointer as a function pointer so we need to sign it.
+ result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
+#endif
}
catch(const char* message) {
syncAllImages();
dyld::log("dyld: launch failed\n");
}
- CRSetCrashLogMessage(NULL);
+ CRSetCrashLogMessage("dyld2 mode");
if (sSkipMain) {
- dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+ if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+ dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
+ }
result = (uintptr_t)&fake_main;
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
}
#include <stdint.h>
#include <sys/stat.h>
#include <dlfcn.h>
+#include <uuid/uuid.h>
#include "ImageLoader.h"
#include "mach-o/dyld_priv.h"
bool mustBeBundle;
bool mustBeDylib;
bool canBePIE;
+ bool enforceIOSMac;
const char* origin; // path for expanding @loader_path
const ImageLoader::RPathChain* rpath; // paths for expanding @rpath
};
typedef void (*ImageCallback)(const struct mach_header* mh, intptr_t slide);
+ typedef void (*LoadImageCallback)(const mach_header* mh, const char* path, bool unloadable);
typedef void (*UndefinedHandler)(const char* symbolName);
typedef const char* (*ImageLocator)(const char* dllName);
extern void registerAddCallback(ImageCallback func);
extern void registerRemoveCallback(ImageCallback func);
extern void registerUndefinedHandler(UndefinedHandler);
+ extern void registerLoadCallback(LoadImageCallback func);
extern void initializeMainExecutable();
extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
const char* getPathFromIndex(unsigned cacheIndex);
#endif
}
-
#include "dyld.h"
#include "dyldLibSystemInterface.h"
#include "DyldSharedCache.h"
+#include "MachOFile.h"
#undef _POSIX_C_SOURCE
#include "dlfcn.h"
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+ #define CPU_SUBTYPE_ARM64_E 2
+#endif
+
+// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
+#if __LP64__
+#define RELOC_SIZE 3
+#define LC_SEGMENT_COMMAND LC_SEGMENT_64
+#define LC_ROUTINES_COMMAND LC_ROUTINES_64
+struct macho_segment_command : public segment_command_64 {};
+struct macho_section : public section_64 {};
+struct macho_routines_command : public routines_command_64 {};
+#else
+#define RELOC_SIZE 2
+#define LC_SEGMENT_COMMAND LC_SEGMENT
+#define LC_ROUTINES_COMMAND LC_ROUTINES
+struct macho_segment_command : public segment_command {};
+struct macho_section : public section {};
+struct macho_routines_command : public routines_command {};
+#endif
+
// this was in dyld_priv.h but it is no longer exported
extern "C" {
extern "C" int _dyld_func_lookup(const char* name, void** address);
+extern "C" void* dlopen_internal(const char* path, int mode, void* callerAddress);
+extern "C" bool dlopen_preflight_internal(const char* path, void* callerAddress);
+extern "C" void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress);
+
+extern "C" void* dlopen_compat(const char* path, int mode);
+extern "C" bool dlopen_preflight_compat(const char* path);
+extern "C" void* dlsym_compat(void* handle, const char* symbolName);
+
+
+
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#define DEPRECATED_APIS_SUPPORTED 0
{"__dyld_dladdr", (void*)dladdr },
{"__dyld_dlclose", (void*)dlclose },
{"__dyld_dlerror", (void*)dlerror },
- {"__dyld_dlopen", (void*)dlopen },
- {"__dyld_dlsym", (void*)dlsym },
- {"__dyld_dlopen_preflight", (void*)dlopen_preflight },
+ {"__dyld_dlopen_internal", (void*)dlopen_internal },
+ {"__dyld_dlsym_internal", (void*)dlsym_internal },
+ {"__dyld_dlopen_preflight_internal", (void*)dlopen_preflight_internal },
+ {"__dyld_dlopen", (void*)dlopen_compat },
+ {"__dyld_dlsym", (void*)dlsym_compat },
+ {"__dyld_dlopen_preflight", (void*)dlopen_preflight_compat },
{"__dyld_image_count", (void*)_dyld_image_count },
{"__dyld_get_image_header", (void*)_dyld_get_image_header },
{"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide },
{"__dyld_objc_notify_register", (void*)_dyld_objc_notify_register },
{"__dyld_get_shared_cache_uuid", (void*)_dyld_get_shared_cache_uuid },
{"__dyld_get_shared_cache_range", (void*)_dyld_get_shared_cache_range },
-
+ {"__dyld_images_for_addresses", (void*)_dyld_images_for_addresses },
+ {"__dyld_register_for_image_loads", (void*)_dyld_register_for_image_loads },
// deprecated
#if DEPRECATED_APIS_SUPPORTED
const void* imageBaseAddress; // not used with OFI created from files
size_t imageLength; // not used with OFI created from files
};
+typedef __NSObjectFileImage* NSObjectFileImage;
VECTOR_NEVER_DESTRUCTED(NSObjectFileImage);
return allImagesIndexedPath(image_index);
}
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
+static void *stripPointer(void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
const struct mach_header * dyld_image_header_containing_address(const void* address)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, address);
+ address = stripPointer(address);
#if SUPPORT_ACCELERATE_TABLES
const mach_header* mh;
const char* path;
context.mustBeBundle = false;
context.mustBeDylib = true;
context.canBePIE = false;
+ context.enforceIOSMac = false;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, address);
+ address = stripPointer(address);
dyld::clearErrorMessage();
ImageLoader* image = dyld::findImageContainingAddress(address);
if ( image != NULL ) {
context.mustBeBundle = true;
context.mustBeDylib = false;
context.canBePIE = false;
+ context.enforceIOSMac = false;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = NULL; // support not yet implemented
}
-bool dlopen_preflight(const char* path)
+bool dlopen_preflight_internal(const char* path, void* callerAddress)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%s)\n", __func__, path);
bool result = false;
std::vector<const char*> rpathsFromCallerImage;
try {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
// for dlopen, use rpath from caller image and from main executable
if ( callerImage != NULL )
context.mustBeBundle = false;
context.mustBeDylib = false;
context.canBePIE = true;
+ context.enforceIOSMac = false;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
}
#endif
-void* dlopen(const char* path, int mode)
+void* dlopen_internal(const char* path, int mode, void* callerAddress)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode);
#if SUPPORT_ACCELERATE_TABLES
if ( dyld::gLogAppAPIs ) {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
const char* shortName;
if ( callerIsNonOSApp(callerAddress, &shortName) ) {
dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode);
std::vector<const char*> rpathsFromCallerImage;
ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage);
try {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
if ( (mode & RTLD_NOLOAD) == 0 ) {
// for dlopen, use rpath from caller image and from main executable
context.mustBeBundle = false;
context.mustBeDylib = false;
context.canBePIE = true;
+ context.enforceIOSMac = false;
context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
context.rpath = &callersRPaths; // rpaths from caller and main executable
bool alreadyLinked = image->isLinked();
bool forceLazysBound = ( (mode & RTLD_NOW) != 0 );
dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex);
- if ( ! alreadyLinked ) {
+ if ( alreadyLinked ) {
+ // upgrade
+ if ( ((mode & RTLD_LOCAL) == 0) && image->hasHiddenExports() )
+ image->setHideExports(false);
+ }
+ else {
// only hide exports if image is not already in use
if ( (mode & RTLD_LOCAL) != 0 )
image->setHideExports(true);
return result;
}
-
-
int dlclose(void* handle)
{
if ( dyld::gLogAPIs )
return 0;
if ( handle == RTLD_DEFAULT )
return 0;
-
+
+#if SUPPORT_ACCELERATE_TABLES
+ if ( dyld::isCacheHandle(handle) ) {
+ dlerrorClear();
+ return 0;
+ }
+#endif
+
ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits
if ( dyld::validImage(image) ) {
dlerrorClear();
if ( dyld::gLogAPIs )
dyld::log("%s(%p, %p)\n", __func__, address, info);
+ // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
+ if ( info == NULL )
+ return 0; // failure
+
+ address = stripPointer(address);
+
CRSetCrashLogMessage("dyld: in dladdr()");
#if SUPPORT_ACCELERATE_TABLES
if ( dyld::dladdrFromCache(address, info) ) {
return NULL;
}
-void* dlsym(void* handle, const char* symbolName)
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
{
if ( dyld::gLogAPIs )
dyld::log("%s(%p, %s)\n", __func__, handle, symbolName);
#if SUPPORT_ACCELERATE_TABLES
if ( dyld::gLogAppAPIs ) {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
const char* shortName;
if ( callerIsNonOSApp(callerAddress, &shortName) ) {
dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName);
if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) {
CRSetCrashLogMessage(NULL);
result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+ // Sign the pointer if it points to a function
+ // Note we only do this if the main executable is arm64e as otherwise we
+ // may end up calling containsAddress on the accelerator tables.
+ if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+ const ImageLoader* symbolImage = image;
+ if (!symbolImage->containsAddress(result)) {
+ symbolImage = dyld::findImageContainingAddress(result);
+ }
+ const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+ if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+ }
+#endif
if ( dyld::gLogAPIs )
dyld::log(" %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result);
return result;
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+ // Sign the pointer if it points to a function
+ // Note we only do this if the main executable is arm64e as otherwise we
+ // may end up calling containsAddress on the accelerator tables.
+ if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+ const ImageLoader* symbolImage = image;
+ if (!symbolImage->containsAddress(result)) {
+ symbolImage = dyld::findImageContainingAddress(result);
+ }
+ const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+ if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+ }
+#endif
if ( dyld::gLogAPIs )
dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result);
return result;
// magic "search what I would see" handle
else if ( handle == RTLD_NEXT ) {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
#if SUPPORT_ACCELERATE_TABLES
const mach_header* mh;
const char* path;
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+ // Sign the pointer if it points to a function
+ // Note we only do this if the main executable is arm64e as otherwise we
+ // may end up calling containsAddress on the accelerator tables.
+ if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+ const ImageLoader* symbolImage = image;
+ if (!symbolImage->containsAddress(result)) {
+ symbolImage = dyld::findImageContainingAddress(result);
+ }
+ const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+ if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+ }
+#endif
if ( dyld::gLogAPIs )
dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result);
return result;
}
// magic "search me, then what I would see" handle
else if ( handle == RTLD_SELF ) {
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
#if SUPPORT_ACCELERATE_TABLES
const mach_header* mh;
const char* path;
if ( sym != NULL ) {
CRSetCrashLogMessage(NULL);
result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+ // Sign the pointer if it points to a function
+ // Note we only do this if the main executable is arm64e as otherwise we
+ // may end up calling containsAddress on the accelerator tables.
+ if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+ const ImageLoader* symbolImage = image;
+ if (!symbolImage->containsAddress(result)) {
+ symbolImage = dyld::findImageContainingAddress(result);
+ }
+ const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+ if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+ }
+#endif
if ( dyld::gLogAPIs )
dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result);
return result;
ImageLoader* callerImage = NULL;
if ( sDynamicInterposing ) {
// only take time to look up caller, if dynamic interposing in use
- void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
callerImage = dyld::findImageContainingAddress(callerAddress);
}
result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+ // Sign the pointer if it points to a function
+ // Note we only do this if the main executable is arm64e as otherwise we
+ // may end up calling containsAddress on the accelerator tables.
+ if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+ const ImageLoader* symbolImage = image;
+ if (!symbolImage->containsAddress(result)) {
+ symbolImage = dyld::findImageContainingAddress(result);
+ }
+ const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+ if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+ result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+ }
+#endif
if ( dyld::gLogAPIs )
dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result);
return result;
return NULL;
}
+// Note this is only here to support ___pthread_abort in libpthread.a
+void* dlsym(void* handle, const char* symbolName) {
+ return dlsym_internal(handle, symbolName, __builtin_return_address(1));
+}
-
-
-
+// <rdar://problem/40352925> *_compat functions are for old binaries that have __dyld section and use it to bypass libdyld.dylib
+void* dlopen_compat(const char* path, int mode)
+{
+ return dlopen_internal(path, mode, (void*)dyld::mainExecutable()->machHeader());
+}
+bool dlopen_preflight_compat(const char* path)
+{
+ return dlopen_preflight_internal(path, (void*)dyld::mainExecutable()->machHeader());
+}
+void* dlsym_compat(void* handle, const char* symbolName)
+{
+ return dlsym_internal(handle, symbolName, (void*)dyld::mainExecutable()->machHeader());
+}
{
//if ( dyld::gLogAPIs )
// dyld::log("%s(%p, %p)\n", __func__, addr, info);
+
+ addr = stripPointer(addr);
#if SUPPORT_ACCELERATE_TABLES
if ( dyld::findUnwindSections(addr, info) )
if ( dyld::gLogAPIs )
dyld::log("%s(%p)\n", __func__, address);
+ address = (void*)stripPointer(address);
+
#if SUPPORT_ACCELERATE_TABLES
const mach_header* mh;
const char* path;
return nullptr;
}
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[])
+{
+ for (unsigned i=0; i < count; ++i) {
+ const void* addr = addresses[i];
+ addr = stripPointer(addr);
+ bzero(&infos[i], sizeof(dyld_image_uuid_offset));
+#if SUPPORT_ACCELERATE_TABLES
+ const mach_header* mh;
+ const char* path;
+ if ( dyld::addressInCache(addr, &mh, &path) ) {
+ infos[i].image = mh;
+ infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)mh;
+ ((dyld3::MachOFile*)mh)->getUuid(infos[i].uuid);
+ break;
+ }
+#endif
+ ImageLoader* image = dyld::findImageContainingAddress(addr);
+ if ( image != nullptr ) {
+ infos[i].image = image->machHeader();
+ infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)(image->machHeader());
+ image->getUUID(infos[i].uuid);
+ }
+ }
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+ if ( dyld::gLogAPIs )
+ dyld::log("%s(%p)\n", __func__, (void *)func);
+ dyld::registerLoadCallback(func);
+}
+
+
#include <string.h>
#include <malloc/malloc.h>
#include <sys/mman.h>
+#include <execinfo.h>
+#include <System/sys/csr.h>
#include <crt_externs.h>
#include <Availability.h>
#include <vproc_priv.h>
#include "ImageLoader.h"
#include "dyldLock.h"
-#include "start_glue.h"
#include "../dyld3/APIs.h"
#include "../dyld3/AllImages.h"
+#include "../dyld3/StartGlue.h"
+#include "../dyld3/Tracing.h"
// this was in dyld_priv.h but it is no longer exported
#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
#define DEPRECATED_APIS_SUPPORTED 0
return (-1);
}
-
-#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
-
-
-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 )
- 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 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;
- *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;
- }
- return false;
-}
-
-#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED
-static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
-{
- const load_command* startCmds = NULL;
- if ( mh->magic == MH_MAGIC_64 )
- startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
- else if ( mh->magic == MH_MAGIC )
- startCmds = (load_command*)((char *)mh + sizeof(mach_header));
- else
- return 0; // not a mach-o file, or wrong endianness
-
- const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
- const dylib_command* dylibCmd;
- const load_command* cmd = startCmds;
- const char* dylibName;
- #if __IPHONE_OS_VERSION_MIN_REQUIRED
- uint32_t foundationVers = 0;
- #else
- uint32_t libSystemVers = 0;
- #endif
- for(uint32_t i = 0; i < mh->ncmds; ++i) {
- const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
- // <rdar://problem/14381579&16050962> sanity check size of command
- if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
- return 0;
- }
- switch ( cmd->cmd ) {
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_LOAD_UPWARD_DYLIB:
- dylibCmd = (dylib_command*)cmd;
- // sanity check dylib command layout
- if ( dylibCmd->dylib.name.offset > cmd->cmdsize )
- return 0;
- dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
- #if __IPHONE_OS_VERSION_MIN_REQUIRED
- if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 )
- foundationVers = dylibCmd->dylib.current_version;
- #else
- if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 )
- libSystemVers = dylibCmd->dylib.current_version;
- #endif
- break;
- }
- cmd = nextCmd;
- }
-
- 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
-
-
-#if __WATCH_OS_VERSION_MIN_REQUIRED
-static uint32_t watchVersToIOSVers(uint32_t vers)
-{
- return vers + 0x00070000;
-}
-
+#if TARGET_OS_WATCH
uint32_t dyld_get_program_sdk_watch_os_version()
{
- if ( gUseDyld3 )
- return dyld3::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 platform;
- uint32_t minOS;
- uint32_t sdk;
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
- if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
- if ( platform == PLATFORM_WATCHOS )
- return sdk;
- }
- return 0;
+ if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+ versionFound = true;
+ retval = sdk_version;
+ }
+ });
+
+ return retval;
}
uint32_t dyld_get_program_min_watch_os_version()
{
- if ( gUseDyld3 )
- return dyld3::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;
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
- if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
- if ( platform == PLATFORM_WATCHOS )
- return minOS; // return raw minOS (not mapped to iOS version)
- }
- return 0;
-}
+ if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+ versionFound = true;
+ retval = min_version;
+ }
+ });
+ return retval;
+}
#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 (gUseDyld3)
+ return dyld3::dyld_get_program_sdk_bridge_os_version();
- if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
- if ( platform == PLATFORM_BRIDGEOS )
- return sdk;
- }
- return 0;
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
+
+ if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+ versionFound = true;
+ retval = sdk_version;
+ }
+ });
+
+ return retval;
}
uint32_t dyld_get_program_min_bridge_os_version()
{
- const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
- uint32_t platform;
- uint32_t minOS;
- uint32_t sdk;
+ if (gUseDyld3)
+ return dyld3::dyld_get_program_min_bridge_os_version();
- if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
- if ( platform == PLATFORM_BRIDGEOS )
- return minOS; // return raw minOS (not mapped to iOS version)
- }
- return 0;
-}
+ __block uint32_t retval = 0;
+ __block bool versionFound = false;
+ dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ if (versionFound) return;
+
+ if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+ versionFound = true;
+ retval = min_version;
+ }
+ });
+ return retval;
+}
#endif
/*
*/
uint32_t dyld_get_sdk_version(const mach_header* mh)
{
- if ( gUseDyld3 )
- return dyld3::dyld_get_sdk_version(mh);
-
- uint32_t platform;
- uint32_t minOS;
- uint32_t sdk;
-
- 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 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
- }
- }
-
-#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
- // MacOSX and iOS have old binaries without version load commmand.
- return deriveSDKVersFromDylibs(mh);
-#endif
+ return dyld3::dyld_get_sdk_version(mh);
}
uint32_t dyld_get_program_sdk_version()
{
- if ( gUseDyld3 )
- return dyld3::dyld_get_program_sdk_version();
-
- return dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader());
+ return dyld3::dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader());
}
uint32_t dyld_get_min_os_version(const struct mach_header* mh)
{
- if ( gUseDyld3 )
- return dyld3::dyld_get_min_os_version(mh);
-
- uint32_t platform;
- uint32_t minOS;
- uint32_t sdk;
-
- 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 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
- }
- }
- return 0;
+ return dyld3::dyld_get_min_os_version(mh);
}
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());
+ return dyld3::dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader());
}
return false;
}
+dyld_platform_t dyld_get_active_platform(void) {
+ if (gUseDyld3)
+ return dyld3::dyld_get_active_platform();
+
+ // HACK
+ // Most of the new version SPIs have pure dyld3 implementations, but
+ // They cannot get to the main executable, so we implement this here
+ // and they can use this by calling ::dyld_get_active_platform() in the root namespace
+ static dyld_platform_t sActivePlatform = 0;
+ if (sActivePlatform) return sActivePlatform;
+
+ dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+ sActivePlatform = platform;
+ //FIXME assert there is only one?
+ });
+ return sActivePlatform;
+}
+
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
+ return dyld3::dyld_get_base_platform(platform);
+}
+
+bool dyld_is_simulator_platform(dyld_platform_t platform) {
+ return dyld3::dyld_is_simulator_platform(platform);
+}
+
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+ return dyld3::dyld_sdk_at_least(mh, version);
+}
+
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+ return dyld3::dyld_minos_at_least(mh, version);
+}
+
+bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+ return dyld3::dyld_sdk_at_least((mach_header*)_NSGetMachExecuteHeader(),version);
+}
+
+bool dyld_program_minos_at_least(dyld_build_version_t version) {
+ return dyld3::dyld_minos_at_least((mach_header*)_NSGetMachExecuteHeader(), version);
+}
+
+// Function that walks through the load commands and calls the internal block for every version found
+// Intended as a fallback for very complex (and rare) version checks, or for tools that need to
+// print our everything for diagnostic reasons
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) {
+ dyld3::dyld_get_image_versions(mh, callback);
+}
+
#if DEPRECATED_APIS_SUPPORTED
static bool isLaunchdOwned()
{
- int64_t val = 0;
- (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
- return ( val != 0 );
+ static bool checked = false;
+ static bool result = false;
+ if ( !checked ) {
+ checked = true;
+ int64_t val = 0;
+ (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
+ result = ( val != 0 );
+ }
+ return result;
}
static void shared_cache_missing()
&vm_allocate,
&mmap,
&__cxa_finalize_ranges
- };
+ };
//
int dladdr(const void* addr, Dl_info* info)
{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLADDR, (uint64_t)addr, 0, 0);
+ int result = 0;
if ( gUseDyld3 )
return dyld3::dladdr(addr, info);
if(p == NULL)
_dyld_func_lookup("__dyld_dladdr", (void**)&p);
- return(p(addr, info));
+ result = p(addr, info);
+ timer.setData4(result);
+ timer.setData5(info != NULL ? info->dli_fbase : 0);
+ timer.setData6(info != NULL ? info->dli_saddr : 0);
+ return result;
}
int dlclose(void* handle)
{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLCLOSE, (uint64_t)handle, 0, 0);
+ int result = 0;
if ( gUseDyld3 )
return dyld3::dlclose(handle);
if(p == NULL)
_dyld_func_lookup("__dyld_dlclose", (void**)&p);
- return(p(handle));
+ result = p(handle);
+ return result;
}
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;
-
- static void* (*p)(const char* path, int) = NULL;
+{
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN, path, mode, 0);
+ void* result = nullptr;
+
+ if ( gUseDyld3 ) {
+ result = dyld3::dlopen_internal(path, mode, __builtin_return_address(0));
+ return result;
+ }
+
+ // dlopen is special. locking is done inside dyld to allow initializer to run without lock
+ DYLD_NO_LOCK_THIS_BLOCK;
+
+ static void* (*p)(const char* path, int, void*) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_dlopen_internal", (void**)&p);
+ result = p(path, mode, __builtin_return_address(0));
+ // use asm block to prevent tail call optimization
+ // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain
+ // <rdar://problem/5313172 dlopen() looks too far up stack, can cause crash>
+ __asm__ volatile("");
+ timer.setData4(result);
+
+#if TARGET_OS_OSX
+ // HACK for iOSMac bringup rdar://40945421
+ if ( result == nullptr && dyld_get_active_platform() == PLATFORM_IOSMAC && csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) {
+ if (hasPerThreadBufferFor_dlerror()) {
+ // first char of buffer is flag whether string (starting at second char) is valid
+ char* buffer = getPerThreadBufferFor_dlerror(2);
+
+ if ( buffer[0] != '\0' && (strstr(&buffer[1], "macOS dylib cannot be loaded into iOSMac process")
+ || strstr(&buffer[1], "mach-o, but not built for iOSMac")) ) {
+ // if valid buffer and contains an iOSMac issue
+ fprintf(stderr, "dyld: iOSMac ERROR: process attempted to dlopen() dylib with macOS dependency: \n");
+ fprintf(stderr, "\tdlerror: %s\n", &buffer[1]);
+ fprintf(stderr, "\tBacktrace:\n");
+
+ void* stackPointers[128];
+ int stackPointersCnt = backtrace(stackPointers, 128);
+ char** symbolicatedStack = backtrace_symbols(stackPointers, stackPointersCnt);
+ for (int32_t i = 0; i < stackPointersCnt; ++i) {
+ fprintf(stderr, "\t\t%s\n", symbolicatedStack[i]);
+ }
+ free(symbolicatedStack);
+ }
+ }
+ }
+#endif
- if(p == NULL)
- _dyld_func_lookup("__dyld_dlopen", (void**)&p);
- void* result = p(path, mode);
- // use asm block to prevent tail call optimization
- // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain
- // <rdar://problem/5313172 dlopen() looks too far up stack, can cause crash>
- __asm__ volatile("");
-
return result;
}
bool dlopen_preflight(const char* path)
{
- if ( gUseDyld3 )
- return dyld3::dlopen_preflight(path);
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN_PREFLIGHT, path, 0, 0);
+ bool result = false;
- DYLD_LOCK_THIS_BLOCK;
- static bool (*p)(const char* path) = NULL;
+ if ( gUseDyld3 ) {
+ result = dyld3::dlopen_preflight_internal(path);
+ return result;
+ }
- if(p == NULL)
- _dyld_func_lookup("__dyld_dlopen_preflight", (void**)&p);
- return(p(path));
+ DYLD_LOCK_THIS_BLOCK;
+ static bool (*p)(const char* path, void* callerAddress) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_dlopen_preflight_internal", (void**)&p);
+ result = p(path, __builtin_return_address(0));
+ timer.setData4(result);
+ return result;
}
void* dlsym(void* handle, const char* symbol)
{
- if ( gUseDyld3 )
- return dyld3::dlsym(handle, symbol);
+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLSYM, handle, symbol, 0);
+ void* result = nullptr;
- DYLD_LOCK_THIS_BLOCK;
- static void* (*p)(void* handle, const char* symbol) = NULL;
+ if ( gUseDyld3 ) {
+ result = dyld3::dlsym_internal(handle, symbol, __builtin_return_address(0));
+ return result;
+ }
- if(p == NULL)
- _dyld_func_lookup("__dyld_dlsym", (void**)&p);
- return(p(handle, symbol));
+ DYLD_LOCK_THIS_BLOCK;
+ static void* (*p)(void* handle, const char* symbol, void *callerAddress) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_dlsym_internal", (void**)&p);
+ result = p(handle, symbol, __builtin_return_address(0));
+ timer.setData4(result);
+ return result;
}
const struct dyld_all_image_infos* _dyld_get_all_image_infos()
return p(length);
}
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[])
+{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_images_for_addresses(count, addresses, infos);
+
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static const void (*p)(unsigned, const void*[], struct dyld_image_uuid_offset[]) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_images_for_addresses", (void**)&p);
+ return p(count, addresses, infos);
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+ if ( gUseDyld3 )
+ return dyld3::_dyld_register_for_image_loads(func);
+
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static const void (*p)(void (*)(const mach_header* mh, const char* path, bool unloadable)) = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_register_for_image_loads", (void**)&p);
+ return p(func);
+}
bool dyld_process_is_restricted()
{
if ( ::stat(path, &statbuf) == -1 )
return NULL;
- if ( statbuf.st_size < length )
+ if ( (size_t)statbuf.st_size < length )
return NULL;
int cache_fd = ::open(path, O_RDONLY);
-
-
}
char* data = (char*)_ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey);
if ( data == NULL ) {
- data = calloc(2,sizeof(void*));
+ long* t = (long*)calloc(2,sizeof(long));
+ data = (char*)t;
_ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data);
}
return data;
#define POINTER_RELOC GENERIC_RELOC_VANILLA
#endif
+#ifndef BIND_OPCODE_THREADED
+#define BIND_OPCODE_THREADED 0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+#define BIND_SUBOPCODE_THREADED_APPLY 0x01
+#endif
+
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
#if TARGET_IPHONE_SIMULATOR
const dyld::SyscallHelpers* gSyscallHelpers = NULL;
return 0;
}
+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 |= (~0ULL) << bit;
+ return result;
+}
+
//
// If the kernel does not load dyld at its preferred address, we need to apply
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
const struct load_command* cmd = cmds;
- const struct macho_segment_command* linkEditSeg = NULL;
+
+ // First look for compressed info and use it if it exists.
+ const struct macho_segment_command* linkEditSeg = NULL;
+ const dyld_info_command* dyldInfoCmd = NULL;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
+ linkEditSeg = seg;
+ break;
+ }
+ case LC_DYLD_INFO_ONLY:
+ dyldInfoCmd = (struct dyld_info_command*)cmd;
+ break;
+ }
+ if (dyldInfoCmd && linkEditSeg)
+ break;
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ if ( linkEditSeg == NULL )
+ throw "dyld missing LINKEDIT";
+
+ // Reset the iterator.
+ cmd = cmds;
+
+ auto getSegmentAtIndex = [cmd_count, cmds](unsigned segIndex) -> const struct macho_segment_command* {
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ if (!segIndex) {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ return seg;
+ }
+ --segIndex;
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ throw "out of bounds command";
+ return 0;
+ };
+
+ auto segActualLoadAddress = [&](unsigned segIndex) -> uintptr_t {
+ const struct macho_segment_command* seg = getSegmentAtIndex(segIndex);
+ return seg->vmaddr + slide;
+ };
+
+#if __has_feature(ptrauth_calls)
+ auto imageBaseAddress = [cmds, cmd_count]() -> uintptr_t {
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND: {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+ return seg->vmaddr;
+ break;
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return 0;
+ };
+#endif
+
+ if (dyldInfoCmd && (dyldInfoCmd->bind_size != 0) ) {
+ if ( dyldInfoCmd->rebase_size != 0 )
+ throw "unthreaded rebases are not supported";
+
+ const uint8_t* linkEditBase = (uint8_t*)(linkEditSeg->vmaddr + slide - linkEditSeg->fileoff);
+
+ const uint8_t* const start = linkEditBase + dyldInfoCmd->bind_off;
+ const uint8_t* const end = &start[dyldInfoCmd->bind_size];
+ const uint8_t* p = start;
+
+ uintptr_t segmentStartAddress = 0;
+ uint64_t segOffset = 0;
+ int segIndex = 0;
+#if __has_feature(ptrauth_calls)
+ uintptr_t fBaseAddress = imageBaseAddress();
+#endif
+ bool done = false;
+
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segmentStartAddress = segActualLoadAddress(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ read_uleb128(p, end);
+ read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ read_uleb128(p, end);
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ uintptr_t address = segmentStartAddress + (uintptr_t)segOffset;
+ uint64_t value = *(uint64_t*)address;
+
+#if __has_feature(ptrauth_calls)
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+
+#if __has_feature(ptrauth_calls)
+ if (isAuthenticated) {
+ // The new value for a rebase is the low 32-bits of the threaded value plus the slide.
+ uint64_t newValue = (value & 0xFFFFFFFF) + slide;
+ // Add in the offset from the mach_header
+ newValue += fBaseAddress;
+ // We have bits to merge in to the discriminator
+ uintptr_t discriminator = diversity;
+ if (hasAddressDiversity) {
+ // First calculate a new discriminator using the address of where we are trying to store the value
+ discriminator = __builtin_ptrauth_blend_discriminator((void*)address, discriminator);
+ }
+ switch (key) {
+ case ptrauth_key_asia:
+ newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asia, discriminator);
+ break;
+ case ptrauth_key_asib:
+ newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asib, discriminator);
+ break;
+ case ptrauth_key_asda:
+ newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asda, discriminator);
+ break;
+ case ptrauth_key_asdb:
+ newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asdb, discriminator);
+ break;
+ }
+ *(uint64_t*)address = newValue;
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ targetValue = targetValue + slide;
+ *(uint64_t*)address = targetValue;
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(uintptr_t);
+ } while ( delta != 0 );
+ break;
+ }
+ default:
+ throw "unknown threaded bind subopcode";
+ }
+ break;
+ default:
+ throw "unknown bind opcode";
+ }
+ }
+ return;
+ }
+
#if __x86_64__
const struct macho_segment_command* firstWritableSeg = NULL;
#endif
case LC_SEGMENT_COMMAND:
{
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
- if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
- linkEditSeg = seg;
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
// use reloc's to rebase all random data pointers
#if __x86_64__
+ if ( firstWritableSeg == NULL )
+ throw "no writable segment in dyld";
const uintptr_t relocBase = firstWritableSeg->vmaddr + slide;
#else
const uintptr_t relocBase = (uintptr_t)mh;
{
// if kernel had to slide dyld, we need to fix up load sensitive locations
// we have to do this before using any global variables
- if ( slide != 0 ) {
- rebaseDyld(dyldsMachHeader, slide);
- }
+ slide = slideOfMainExecutable(dyldsMachHeader);
+ bool shouldRebase = slide != 0;
+#if __has_feature(ptrauth_calls)
+ shouldRebase = true;
+#endif
+ if ( shouldRebase ) {
+ rebaseDyld(dyldsMachHeader, slide);
+ }
// allow dyld to use mach messaging
mach_init();
struct LibSystemHelpers
{
uintptr_t version;
- void (*acquireGlobalDyldLock)();
- void (*releaseGlobalDyldLock)();
+ void (*acquireGlobalDyldLock)(void);
+ void (*releaseGlobalDyldLock)(void);
char* (*getThreadBufferFor_dlerror)(size_t sizeRequired);
// addded in version 2
void* (*malloc)(size_t);
void (*free)(void*);
int (*cxa_atexit)(void (*)(void*), void*, void*);
// addded in version 3
- void (*dyld_shared_cache_missing)();
- void (*dyld_shared_cache_out_of_date)();
+ void (*dyld_shared_cache_missing)(void);
+ void (*dyld_shared_cache_out_of_date)(void);
// addded in version 4
- void (*acquireDyldInitializerLock)();
- void (*releaseDyldInitializerLock)();
+ void (*acquireDyldInitializerLock)(void);
+ void (*releaseDyldInitializerLock)(void);
// added in version 5
int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*));
int (*pthread_setspecific)(pthread_key_t, const void*);
// added in version 9
void* startGlueToCallExit;
// added in version 10
- bool (*hasPerThreadBufferFor_dlerror)();
+ bool (*hasPerThreadBufferFor_dlerror)(void);
// added in version 11
- bool (*isLaunchdOwned)();
+ bool (*isLaunchdOwned)(void);
// added in version 12
kern_return_t (*vm_alloc)(vm_map_t task, vm_address_t* addr, vm_size_t size, int flags);
void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
}
else {
if ( size > DYLD_POOL_CHUNK_SIZE ) {
- dyld::log("dyld malloc overflow: size=%zu\n", size);
- exit(1);
+ dyld::log("dyld malloc overflow: size=%lu\n", size);
+ dyld::halt("dyld malloc overflow\n");
}
size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align
uint8_t* result = currentPool->current;
vm_address_t addr = 0;
kern_return_t r = vm_allocate(mach_task_self(), &addr, DYLD_POOL_CHUNK_SIZE, VM_FLAGS_ANYWHERE);
if ( r != KERN_SUCCESS ) {
- dyld::log("out of address space for dyld memory pool\n");
- exit(1);
+ dyld::halt("out of address space for dyld memory pool\n");
}
dyld_static_pool* newPool = (dyld_static_pool*)addr;
newPool->previousPool = NULL;
currentPool = newPool;
if ( (currentPool->current + size) > currentPool->end ) {
dyld::log("dyld memory pool exhausted: size=%lu\n", size);
- exit(1);
+ dyld::halt("dyld memory pool exhausted\n");
}
result = currentPool->current;
currentPool->current += size;
* Copyright (c) 1999-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,
* 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@
*/
/*
*
* | STRING AREA |
* +-------------+
- * | 0 |
+ * | 0 |
* +-------------+
* | apple[n] |
* +-------------+
* :
* +-------------+
- * | apple[0] |
- * +-------------+
+ * | apple[0] |
+ * +-------------+
* | 0 |
* +-------------+
* | env[n] |
movl %esp,%ebp # pointer to base of kernel frame
andl $-16,%esp # force SSE alignment
subl $32,%esp # room for locals and outgoing parameters
-
+
call L__dyld_start_picbase
-L__dyld_start_picbase:
+L__dyld_start_picbase:
popl %ebx # set %ebx to runtime value of picbase
movl Lmh-L__dyld_start_picbase(%ebx), %ecx # ecx = prefered load address
addl %ebx, %ecx # ecx = actual load address
# call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
movl %edx,(%esp) # param1 = app_mh
- movl 4(%ebp),%eax
+ movl 4(%ebp),%eax
movl %eax,4(%esp) # param2 = argc
- lea 8(%ebp),%eax
+ lea 8(%ebp),%eax
movl %eax,8(%esp) # param3 = argv
movl %ebx,12(%esp) # param4 = slide
movl %ecx,16(%esp) # param5 = actual load address
lea 28(%esp),%eax
movl %eax,20(%esp) # param6 = &startGlue
- call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
+ call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
movl 28(%esp),%edx
cmpl $0,%edx
jne Lnew
movl $0,%ebp # restore ebp back to zero
jmp *%eax # jump to the entry point
- # LC_MAIN case, set up stack for call to main()
+ # LC_MAIN case, set up stack for call to main()
Lnew: movl 4(%ebp),%ebx
movl %ebx,(%esp) # main param1 = argc
leal 8(%ebp),%ecx
movl %ebx,12(%esp) # main param4 = apple
pushl %edx # simulate return address into _start in libdyld
jmp *%eax # jump to main(argc,argv,env,apple) with return address set to _start
-#endif
+#endif
#if !TARGET_IPHONE_SIMULATOR
.data
-__dyld_start_static_picbase:
+__dyld_start_static_picbase:
.long L__dyld_start_picbase
Lmh: .long ___dso_handle
#endif
#if !TARGET_IPHONE_SIMULATOR
.data
.align 3
-__dyld_start_static:
+__dyld_start_static:
.quad __dyld_start
#endif
movq %rsp,%rbp # pointer to base of kernel frame
andq $-16,%rsp # force SSE alignment
subq $16,%rsp # room for local variables
-
+
# call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
movl 8(%rbp),%esi # param2 = argc into %esi
leaq 16(%rbp),%rdx # param3 = &argv[0] into %rdx
addq $8,%rsp # remove the mh argument, and debugger end frame marker
movq $0,%rbp # restore ebp back to zero
jmp *%rax # jump to the entry point
-
- # LC_MAIN case, set up stack for call to main()
+
+ # LC_MAIN case, set up stack for call to main()
Lnew: addq $16,%rsp # remove local variables
pushq %rdi # simulate return address into _start in libdyld
movq 8(%rbp),%rdi # main param1 = argc into %rdi
.syntax unified
.data
.align 2
-__dyld_start_static_picbase:
+__dyld_start_static_picbase:
.long L__dyld_start_picbase
-
+
// Hack to make ___dso_handle work
// Without this local symbol, assembler will error out about in subtraction expression
// The real ___dso_handle (non-weak) sythesized by the linker
add r2, r8, #8 // r2 = argv
ldr r4, Lmh
-L3: add r4, r4, pc
+L3: add r4, r4, pc
str r4, [sp, #0] // [sp] = dyld_mh
add r4, sp, #12
str r4, [sp, #4] // [sp+4] = &startGlue
-
+
bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
ldr r5, [sp, #12]
cmp r5, #0
bne Lnew
-
+
// traditional case, clean up stack and jump to result
add sp, r8, #4 // remove the mach_header argument.
bx r0 // jump to the program's entry point
mov r5, r0 // save address of main() for later use
ldr r0, [r8, #4] // main param1 = argc
add r1, r8, #8 // main param2 = argv
- add r2, r1, r0, lsl #2
+ add r2, r1, r0, lsl #2
add r2, r2, #4 // main param3 = &env[0]
mov r3, r2
Lapple: ldr r4, [r3]
#if __arm64__
.data
.align 3
-__dso_static:
+__dso_static:
.quad ___dso_handle
.text
stp x1, x0, [sp, #-16]! // make aligned terminating frame
mov fp, sp // set up fp to point to terminating frame
sub sp, sp, #16 // make room for local variables
- ldr x0, [x28] // get app's mh into x0
- ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
- add x2, x28, #16 // get argv into x2
+#if __LP64__
+ ldr x0, [x28] // get app's mh into x0
+ ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
+ add x2, x28, #16 // get argv into x2
+#else
+ ldr w0, [x28] // get app's mh into x0
+ ldr w1, [x28, #4] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
+ add w2, w28, #8 // get argv into x2
+#endif
adrp x4,___dso_handle@page
add x4,x4,___dso_handle@pageoff // get dyld's mh in to x4
adrp x3,__dso_static@page
ldr x3,[x3,__dso_static@pageoff] // get unslid start of dyld
sub x3,x4,x3 // x3 now has slide of dyld
mov x5,sp // x5 has &startGlue
-
+
// call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
mov x16,x0 // save entry point address in x16
+#if __LP64__
ldr x1, [sp]
+#else
+ ldr w1, [sp]
+#endif
cmp x1, #0
b.ne Lnew
// LC_UNIXTHREAD way, clean up stack and jump to result
- add sp, x28, #8 // restore unaligned stack pointer without app mh
- br x16 // jump to the program's entry point
+#if __LP64__
+ add sp, x28, #8 // restore unaligned stack pointer without app mh
+#else
+ add sp, x28, #4 // restore unaligned stack pointer without app mh
+#endif
+#if __arm64e__
+ braaz x16 // jump to the program's entry point
+#else
+ br x16 // jump to the program's entry point
+#endif
// LC_MAIN case, set up stack for call to main()
Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib
- ldr x0, [x28, #8] // main param1 = argc
- add x1, x28, #16 // main param2 = argv
- add x2, x1, x0, lsl #3
- add x2, x2, #8 // main param3 = &env[0]
+#if __LP64__
+ ldr x0, [x28, #8] // main param1 = argc
+ add x1, x28, #16 // main param2 = argv
+ add x2, x1, x0, lsl #3
+ add x2, x2, #8 // main param3 = &env[0]
mov x3, x2
Lapple: ldr x4, [x3]
add x3, x3, #8
+#else
+ ldr w0, [x28, #4] // main param1 = argc
+ add x1, x28, #8 // main param2 = argv
+ add x2, x1, x0, lsl #2
+ add x2, x2, #4 // main param3 = &env[0]
+ mov x3, x2
+Lapple: ldr w4, [x3]
+ add x3, x3, #4
+#endif
cmp x4, #0
b.ne Lapple // main param4 = apple
- br x16
+#if __arm64e__
+ braaz x16
+#else
+ br x16
+#endif
#endif // __arm64__
bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value);
void (*OSMemoryBarrier)(void);
void* (*getProcessInfo)(void); // returns dyld_all_image_infos*;
- int* (*errnoAddress)();
- uint64_t (*mach_absolute_time)();
+ int* (*errnoAddress)(void);
+ uint64_t (*mach_absolute_time)(void);
// Added in version 2
kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t);
// Added in version 3
void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
// Added in version 5
int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize);
- int (*getpid)();
+ int (*getpid)(void);
kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
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);
+ // Add in version 8
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);
+ // Add in version 9
+ uint64_t (*kdebug_trace_string)(uint32_t debugid, uint64_t str_id, const char *str);
+ // Add in version 10
+ int (*amfi_check_dyld_policy_self)(uint64_t input_flags, uint64_t* output_flags);
+ // Add in version 11
+ void (*notifyMonitoringDyldMain)(void);
+ void (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
+ // Add in version 12
+ void (*mach_msg_destroy)(mach_msg_header_t *msg);
+ kern_return_t (*mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
+ kern_return_t (*mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
};
extern const struct SyscallHelpers* gSyscallHelpers;
#include "mach-o/dyld_gdb.h"
#include "mach-o/dyld_images.h"
#include "mach-o/dyld_process_info.h"
+#include "Tracing.h"
#include "ImageLoader.h"
#include "dyld.h"
static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
{
+ dyld3::ScopedTimer(DBG_DYLD_GDB_IMAGE_NOTIFIER, 0, 0, 0);
uint64_t machHeaders[infoCount];
for (uint32_t i=0; i < infoCount; ++i) {
machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress);
struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info")))
= {
- 15, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
- XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos,
+ 15, 0, {NULL}, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
+ XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos,
0, 0, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
- 0, 0, "/usr/lib/dyld", {0}, {0}
+ 0, {0}, "/usr/lib/dyld", {0}, {0}
};
struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
#include "dyld_images.h"
#include "dyld_priv.h"
+#include "Tracing.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, size_t size)) {
- 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), local_size);
- 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;
+RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0) {}
+bool RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, bool shared) {
+ vm_prot_t cur_protection = VM_PROT_NONE;
+ vm_prot_t max_protection = VM_PROT_NONE;
+ if (_size == 0) {
+ _kr = KERN_NO_SPACE;
+ return false;
+ }
+ _localAddress = 0;
+ _kr = mach_vm_remap(mach_task_self(),
+ &_localAddress,
+ _size,
+ 0, // mask
+ VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | (shared ? 0 : VM_FLAGS_RESILIENT_CODESIGN),
+ task,
+ remote_address,
+ !shared,
+ &cur_protection,
+ &max_protection,
+ VM_INHERIT_NONE);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_REMAP, _localAddress, (uint64_t)_size, _kr, remote_address);
+ if (shared && (cur_protection != (VM_PROT_READ|VM_PROT_WRITE))) {
+ if (_kr == KERN_SUCCESS && _localAddress != 0) {
+ _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
}
+ _localAddress = 0;
+ _kr = KERN_PROTECTION_FAILURE;
}
+ return (_kr == KERN_SUCCESS);
}
-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, size_t size) {
- block(*reinterpret_cast<T *>(buffer));
- });
+RemoteBuffer::RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation)
+ : _localAddress(0), _size(remote_size), _kr(KERN_SUCCESS) {
+ // Try the initial map
+ if (map(task, remote_address, shared)) return;
+ // It failed, try to calculate the largest size that can fit in the same page as the remote_address
+ uint64_t newSize = PAGE_SIZE - remote_address%PAGE_SIZE;;
+ // If truncation is allowed and the newSize is different than the original size try that
+ if (!allow_truncation && newSize != _size) return;
+ _size = newSize;
+ if (map(task, remote_address, shared)) return;
+ // That did not work, null out the buffer
+ _size = 0;
+ _localAddress = 0;
}
-};
+RemoteBuffer::~RemoteBuffer() {
+ if (_localAddress) {
+ _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
+ }
+}
+void *RemoteBuffer::getLocalAddress() { return (void *)_localAddress; }
+size_t RemoteBuffer::getSize() { return _size; }
+kern_return_t RemoteBuffer::getKernelReturn() { return _kr; }
+
+void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) {
+ kern_return_t krSink = KERN_SUCCESS;
+ if (kr == nullptr) {
+ kr = &krSink;
+ }
+ RemoteBuffer buffer(task, remote_address, remote_size, shared, allow_truncation);
+ *kr = buffer.getKernelReturn();
+ if (*kr == KERN_SUCCESS) {
+ block(buffer.getLocalAddress(), buffer.getSize());
+ }
+}
+
//
// Opaque object returned by _dyld_process_info_create()
//
+struct __attribute__((visibility("hidden"))) dyld_process_info_deleter { // deleter
+ // dyld_process_info_deleter() {};
+ // dyld_process_info_deleter(const dyld_process_info_deleter&) { }
+ // dyld_process_info_deleter(dyld_process_info_deleter&) {}
+ // dyld_process_info_deleter(dyld_process_info_deleter&&) {}
+ void operator()(dyld_process_info_base* p) const {
+ if (p) {
+ free(p);
+ }
+ };
+};
+
+static dyld_process_info_deleter deleter;
+typedef std::unique_ptr<dyld_process_info_base, dyld_process_info_deleter> dyld_process_info_ptr;
+
struct __attribute__((visibility("hidden"))) dyld_process_info_base {
- static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr);
- static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr);
+ template<typename T1, typename T2>
+ static dyld_process_info_ptr make(task_t task, const T1& allImageInfo, uint64_t timestamp, kern_return_t* kr);
+ template<typename T>
+ static dyld_process_info_ptr makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr);
- uint32_t& retainCount() const { return _retainCount; }
+ std::atomic<uint32_t>& retainCount() const { return _retainCount; }
dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); }
dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); }
void forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const;
void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const;
+ void retain()
+ {
+ _retainCount++;
+ }
+
+ void release()
+ {
+ uint32_t newCount = --_retainCount;
+
+ if ( newCount == 0 ) {
+ free(this);
+ }
+ }
+
private:
struct ImageInfo {
uuid_t uuid;
static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); }
kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal);
+
kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath);
bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); }
const char* copySegmentName(const char*);
void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size);
+ kern_return_t addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH);
void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func);
kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func);
- mutable uint32_t _retainCount;
+ mutable std::atomic<uint32_t> _retainCount;
const uint32_t _cacheInfoOffset;
const uint32_t _stateInfoOffset;
const uint32_t _imageInfosOffset;
{
}
-
-dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr)
+template<typename T1, typename T2>
+dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allImageInfo, uint64_t timestamp, kern_return_t* kr)
{
- // figure out how many path strings will need to be copied and their size
- const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos();
- bool sameCacheAsThisProcess = !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) {
- if ( !inCache(imageArray[i].imageFilePath) )
- ++countOfPathsNeedingCopying;
- }
- }
- else {
- countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1;
- }
- unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1;
+ __block dyld_process_info_ptr result = nullptr;
- // allocate result object
- size_t allocationSize = sizeof(dyld_process_info_base)
- + sizeof(dyld_process_cache_info)
- + sizeof(dyld_process_state_info)
- + sizeof(ImageInfo)*(imageCountWithDyld)
- + sizeof(SegmentInfo)*imageCountWithDyld*5
- + countOfPathsNeedingCopying*PATH_MAX;
- void* storage = malloc(allocationSize);
- dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
-
- // fill in base info
- dyld_process_cache_info* cacheInfo = obj->cacheInfo();
- memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16);
- cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress;
- cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion;
- // if no cache is used, allImageInfo has all zeros for cache UUID
- cacheInfo->noCache = true;
- for (int i=0; i < 16; ++i) {
- if ( cacheInfo->cacheUUID[i] != 0 ) {
- cacheInfo->noCache = false;
+ // bail out of dyld is too old
+ if ( allImageInfo.version < 15 ) {
+ *kr = KERN_FAILURE;
+ return nullptr;
+ }
+
+ // Check if the process is suspended
+ if (allImageInfo.infoArrayChangeTimestamp == 0) {
+ result = dyld_process_info_base::makeSuspended<T1>(task, allImageInfo, kr);
+ // If we have a result return it, otherwise rescan
+ if (result) {
+ // If it returned the process is suspended and there is nothing more to do
+ return std::move(result);
+ } else {
+ // Check to see if the process change timestamp is greater than 0, if not then sleep to let the process
+ // finish initializing
+ if (allImageInfo.infoArrayChangeTimestamp == 0) {
+ usleep(1000 * 50); // 50ms
+ }
}
}
- dyld_process_state_info* stateInfo = obj->stateInfo();
- stateInfo->timestamp = allImageInfo.infoArrayChangeTimestamp;
- stateInfo->imageCount = imageCountWithDyld;
- stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1);
- if ( allImageInfo.infoArray != 0 )
- stateInfo->dyldState = dyld_process_state_dyld_initialized;
- if ( allImageInfo.libSystemInitialized != 0 ) {
- stateInfo->dyldState = dyld_process_state_libSystem_initialized;
- if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount )
- stateInfo->dyldState = dyld_process_state_program_running;
- }
- if ( allImageInfo.errorMessage != 0 )
- stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated;
+ // Test to see if there are no changes and we can exit early
+ if (timestamp != 0 && timestamp == allImageInfo.infoArrayChangeTimestamp) {
+ *kr = KERN_SUCCESS;
+ return nullptr;
+ }
+
+ for(uint32_t i = 0; i < 10; ++i) {
+ uint64_t currentTimestamp = allImageInfo.infoArrayChangeTimestamp;
+ mach_vm_address_t infoArray = allImageInfo.infoArray;
+ if (currentTimestamp == 0) continue;
+ if (infoArray == 0) {
+ // Check if the task is suspended mid dylib load and exit early
+ mach_task_basic_info ti;
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+ if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+ continue;
+ }
- // fill in info for dyld
- if ( allImageInfo.dyldPath != 0 ) {
- if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) {
- if ( kr != NULL )
- *kr = r;
- goto fail;
- }
- }
+ // The task is suspended, exit
+ if (ti.suspend_count != 0) {
+ // Not exactly correct, but conveys that operation may succeed in the future
+ *kr = KERN_RESOURCE_SHORTAGE;
+ return nullptr;
+ }
+ continue;
+ };
+
+ // For the moment we are going to truncate any image list longer than 8192 because some programs do
+ // terrible things that corrupt their own image lists and we need to stop clients from crashing
+ // reading them. We can try to do something more advanced in the future. rdar://27446361
+ uint32_t imageCount = allImageInfo.infoArrayCount;
+ imageCount = MIN(imageCount, 8192);
+ size_t imageArraySize = imageCount * sizeof(T2);
+
+ withRemoteBuffer(task, infoArray, imageArraySize, false, false, kr, ^(void *buffer, size_t size) {
+ // figure out how many path strings will need to be copied and their size
+ T2* imageArray = (T2 *)buffer;
+ const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos();
+ bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion
+ && !myInfo->processDetachedFromSharedRegion
+ && ((memcmp(myInfo->sharedCacheUUID, &allImageInfo.sharedCacheUUID[0], 16) == 0)
+ && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide));
+ unsigned countOfPathsNeedingCopying = 0;
+ if ( sameCacheAsThisProcess ) {
+ for (uint32_t i=0; i < imageCount; ++i) {
+ if ( !inCache(imageArray[i].imageFilePath) )
+ ++countOfPathsNeedingCopying;
+ }
+ }
+ else {
+ countOfPathsNeedingCopying = imageCount+1;
+ }
+ unsigned imageCountWithDyld = imageCount+1;
+
+ // allocate result object
+ size_t allocationSize = sizeof(dyld_process_info_base)
+ + sizeof(dyld_process_cache_info)
+ + sizeof(dyld_process_state_info)
+ + sizeof(ImageInfo)*(imageCountWithDyld)
+ + sizeof(SegmentInfo)*imageCountWithDyld*5
+ + countOfPathsNeedingCopying*PATH_MAX;
+ void* storage = malloc(allocationSize);
+ auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize), deleter);
+ //info = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
+
+ // fill in base info
+ dyld_process_cache_info* cacheInfo = info->cacheInfo();
+ memcpy(cacheInfo->cacheUUID, &allImageInfo.sharedCacheUUID[0], 16);
+ cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress;
+ cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion;
+ // if no cache is used, allImageInfo has all zeros for cache UUID
+ cacheInfo->noCache = true;
+ for (int i=0; i < 16; ++i) {
+ if ( cacheInfo->cacheUUID[i] != 0 ) {
+ cacheInfo->noCache = false;
+ }
+ }
- // fill in info for each image
- for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) {
- if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) {
- if ( kr != NULL )
- *kr = r;
- goto fail;
- }
- }
+ dyld_process_state_info* stateInfo = info->stateInfo();
+ stateInfo->timestamp = currentTimestamp;
+ stateInfo->imageCount = imageCountWithDyld;
+ stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1);
+ stateInfo->dyldState = dyld_process_state_dyld_initialized;
- // sanity check internal data did not overflow
- if ( obj->invalid() )
- goto fail;
+ if ( allImageInfo.libSystemInitialized != 0 ) {
+ stateInfo->dyldState = dyld_process_state_libSystem_initialized;
+ if ( allImageInfo.initialImageCount != imageCount ) {
+ stateInfo->dyldState = dyld_process_state_program_running;
+ }
+ }
+ if ( allImageInfo.errorMessage != 0 ) {
+ stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated;
+ }
+ // fill in info for dyld
+ if ( allImageInfo.dyldPath != 0 ) {
+ if ((*kr = info->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL))) {
+ result = nullptr;
+ return;
+ }
+ }
+ // fill in info for each image
+ for (uint32_t i=0; i < imageCount; ++i) {
+ if ((*kr = info->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL))) {
+ result = nullptr;
+ return;
+ }
+ }
+ // sanity check internal data did not overflow
+ if ( info->invalid() ) {
+ *kr = KERN_FAILURE;
+ result = nullptr;
+ return;
+ }
- return obj;
+ result = std::move(info);
+ });
-fail:
- free(obj);
- return NULL;
+ if (result) break;
+ }
+
+ return std::move(result);
}
-dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr)
+template<typename T>
+dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr)
{
pid_t pid;
- kern_return_t result = pid_for_task(task, &pid);
- if ( result != KERN_SUCCESS ) {
- if ( kr != NULL )
- *kr = result;
+ if ((*kr = pid_for_task(task, &pid))) {
return NULL;
}
+ mach_task_basic_info ti;
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+ if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+ return nullptr;
+ }
+
+ // The task is not suspended, exit
+ if (ti.suspend_count == 0) {
+ return nullptr;
+ }
+
__block unsigned imageCount = 0; // main executable and dyld
__block uint64_t mainExecutableAddress = 0;
__block uint64_t dyldAddress = 0;
vm_region_basic_info_data_64_t info;
mach_port_t objectName;
unsigned int infoCount = VM_REGION_BASIC_INFO_COUNT_64;
- result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
- (vm_region_info_t)&info, &infoCount, &objectName);
- if ( result != KERN_SUCCESS )
+ if (kern_return_t r = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
+ (vm_region_info_t)&info, &infoCount, &objectName)) {
break;
+ }
if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) )
continue;
// read start of vm region to verify it is a mach header
- withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){
+ withRemoteObject(task, address, false, 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
+ sizeof(SegmentInfo)*imageCount*5
+ imageCount*PATH_MAX;
void* storage = malloc(allocationSize);
- dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new()
-
+ auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base(imageCount, allocationSize), deleter);
// fill in base info
dyld_process_cache_info* cacheInfo = obj->cacheInfo();
bzero(cacheInfo->cacheUUID, 16);
// fill in info for dyld
if ( dyldAddress != 0 ) {
- if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPath) ) {
- if ( kr != NULL )
- *kr = r;
- free(obj);
- return NULL;
+ if ((*kr = obj->addDyldImage(task, dyldAddress, 0, dyldPath))) {
+ return nullptr;
}
}
// fill in info for each image
if ( mainExecutableAddress != 0 ) {
- if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) {
- if ( kr != NULL )
- *kr = r;
- free(obj);
- return NULL;
+ if ((*kr = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath))) {
+ return nullptr;
}
}
+ if (allImageInfo.infoArrayChangeTimestamp != 0) {
+ return nullptr;
+ }
+
+ count = MACH_TASK_BASIC_INFO_COUNT;
+ if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+ return nullptr;
+ }
+
+ // The task is not suspended, exit
+ if (ti.suspend_count == 0) {
+ return nullptr;
+ }
+
return obj;
}
const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr)
{
__block const char* retval = NULL;
- withRemoteBuffer(task, stringAddressInTask, PATH_MAX, true, kr, ^(void *buffer, size_t size) {
+ withRemoteBuffer(task, stringAddressInTask, PATH_MAX, false, true, kr, ^(void *buffer, size_t size) {
retval = addString(static_cast<const char *>(buffer), size);
});
-
return retval;
}
kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal)
{
+ kern_return_t kr = KERN_SUCCESS;
_curImage->loadAddress = imageAddress;
_curImage->segmentStartIndex = _curSegmentIndex;
if ( imagePathLocal != NULL ) {
_curImage->path = (const char*)imagePath;
}
else {
- kern_return_t kr;
_curImage->path = copyPath(task, imagePath, &kr);
- if ( kr )
+ if ( kr != KERN_SUCCESS)
return kr;
}
if ( sameCacheAsThisProcess && inCache(imageAddress) ) {
addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024);
}
else {
- __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, size_t size) {
- addInfoFromLoadCommands((mach_header*)buffer, imageAddress, size);
- });
- });
- if (kr != KERN_SUCCESS) {
+ kr = addInfoFromRemoteLoadCommands(task, imageAddress);
+ if ( kr != KERN_SUCCESS)
return kr;
- }
}
_curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
_curImage++;
}
+kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH) {
+ __block kern_return_t kr = KERN_SUCCESS;
+ __block size_t headerPagesSize = 0;
+ __block bool done = false;
+
+ //Since the minimum we can reasonably map is a page, map that.
+ withRemoteBuffer(task, remoteMH, PAGE_SIZE, false, false, &kr, ^(void * buffer, size_t size) {
+ const mach_header* mh = (const mach_header*)buffer;
+ headerPagesSize = sizeof(mach_header) + mh->sizeofcmds;
+ if (headerPagesSize <= PAGE_SIZE) {
+ addInfoFromLoadCommands(mh, remoteMH, size);
+ done = true;
+ }
+ });
+
+ //The load commands did not fit in the first page, but now we know the size, so remap and try again
+ if (!done) {
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+ withRemoteBuffer(task, remoteMH, headerPagesSize, false, false, &kr, ^(void * buffer, size_t size) {
+ addInfoFromLoadCommands((mach_header*)buffer, remoteMH, size);
+ });
+ }
+
+ return kr;
+}
+
kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
{
__block kern_return_t kr = KERN_SUCCESS;
}
else {
_curImage->path = copyPath(task, dyldPathAddress, &kr);
- if ( kr )
+ if ( kr != KERN_SUCCESS)
return kr;
}
- 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, size_t size) {
- addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, size);
- });
- });
- if (kr != KERN_SUCCESS) {
+ kr = addInfoFromRemoteLoadCommands(task, dyldAddress);
+ if ( kr != KERN_SUCCESS)
return kr;
- }
_curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
_curImage++;
for (const ImageInfo* p = _firstImage; p < _curImage; ++p) {
if ( p->loadAddress == machHeaderAddress ) {
uint64_t slide = 0;
- for (int i=0; i < p->segmentsCount; ++i) {
+ for (uint32_t i=0; i < p->segmentsCount; ++i) {
const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
if ( strcmp(seg->name, "__TEXT") == 0 ) {
slide = machHeaderAddress - seg->addr;
break;
}
}
- for (int i=0; i < p->segmentsCount; ++i) {
+ for (uint32_t i=0; i < p->segmentsCount; ++i) {
const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
callback(seg->addr + slide, seg->size, seg->name);
}
}
}
-
-
-
-
-// Implementation that works with existing dyld data structures
-static dyld_process_info _dyld_process_info_create_inner(task_t task, uint64_t timestamp, kern_return_t* kr)
+dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr)
{
- if ( kr != NULL )
- *kr = KERN_SUCCESS;
+ __block dyld_process_info result = nullptr;
+ kern_return_t krSink = KERN_SUCCESS;
+ if (kr == nullptr) {
+ kr = &krSink;
+ }
+ *kr = KERN_SUCCESS;
task_dyld_info_data_t task_dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
- if ( kr != NULL )
- *kr = r;
- return NULL;
+ *kr = r;
+ return nullptr;
}
//The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS)
- return NULL;
+ return nullptr;
if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) )
- return NULL;
-
- // read all_image_infos struct
- dyld_all_image_infos_64 allImageInfo64;
- mach_vm_size_t readSize = task_dyld_info.all_image_info_size;
- if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
- if ( kr != NULL )
- *kr = r;
- return NULL;
- }
- if ( allImageInfo64.infoArrayCount == 0 ) {
- // could be task was launch suspended or still launching, wait a moment to see
- usleep(1000 * 50); // 50ms
- if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
- if ( kr != NULL )
- *kr = r;
- return NULL;
- }
- // if infoArrayCount is still zero, then target was most likely launched suspended
- if ( allImageInfo64.infoArrayCount == 0 )
- return dyld_process_info_base::makeSuspended(task, kr);
- }
-
- // bail out of dyld is too old
- if ( allImageInfo64.version < 15 ) {
- if ( kr != NULL )
- *kr = KERN_INVALID_HOST;
- return NULL;
- }
-
- // normalize by expanding 32-bit all_image_infos into 64-bit one
- uint32_t imageCount = allImageInfo64.infoArrayCount;
- size_t imageArraySize = imageCount * sizeof(dyld_image_info_64);
- if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
- const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64;
- dyld_all_image_infos_64 info64;
- bzero(&info64, sizeof(info64));
- info64.version = allImageInfo32->version;
- info64.infoArrayCount = allImageInfo32->infoArrayCount;
- info64.infoArray = allImageInfo32->infoArray;
- info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion;
- info64.libSystemInitialized = allImageInfo32->libSystemInitialized;
- info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress;
- info64.initialImageCount = allImageInfo32->initialImageCount;
- info64.uuidArrayCount = allImageInfo32->uuidArrayCount;
- info64.uuidArray = allImageInfo32->uuidArray;
- info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress;
- info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide;
- info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp;
- info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress;
- info64.dyldPath = allImageInfo32->dyldPath;
- memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16);
- allImageInfo64 = info64;
- imageCount = allImageInfo64.infoArrayCount;
- imageArraySize = imageCount * sizeof(dyld_image_info_32);
- }
-
- // don't do any (more) work if target process's dyld timestamp has not changed since previous query
- if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) {
- if ( kr != NULL )
- *kr = KERN_SUCCESS;
- return NULL;
- }
-
- // For the moment we are going to truncate any image list longer than 8192 because some programs do
- // terrible things that corrupt their own image lists and we need to stop clients from crashing
- // reading them. We can try to do something more advanced in the future. rdar://27446361
- imageCount = MIN(imageCount, 8192);
-
- // read image array
- if ( allImageInfo64.infoArray == 0 ) {
- // dyld is in middle of updating image list, try again
- return NULL;
- }
- dyld_image_info_64 imageArray64[imageCount];
- if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) {
- // if image array moved, try whole thing again
- if ( kr != NULL ) {
- *kr = r;
- }
- return NULL;
- }
- // normalize by expanding 32-bit image_infos into 64-bit ones
- if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
- const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64;
- dyld_image_info_64 tempArray[imageCount];
- for (uint32_t i=0; i < imageCount; ++i) {
- tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress;
- tempArray[i].imageFilePath = imageArray32[i].imageFilePath;
- tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate;
- }
- memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount);
- }
-
- // create object based on local copy of all image infos and image array
- dyld_process_info result = dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr);
-
- // verify nothing has changed by re-reading all_image_infos struct and checking timestamp
- if ( result != NULL ) {
- dyld_all_image_infos_64 allImageInfo64again;
- readSize = task_dyld_info.all_image_info_size;
- if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64again, &readSize) ) {
- if ( kr != NULL )
- *kr = r;
- free((void*)result);
- return NULL;
- }
- uint64_t doneTimeStamp = allImageInfo64again.infoArrayChangeTimestamp;
- if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
- const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64again;
- doneTimeStamp = allImageInfo32->infoArrayChangeTimestamp;
+ return nullptr;
+
+ // We use a true shared memory buffer here, that way by making sure that libdyld in both processes
+ // reads and writes the the timestamp atomically we can make sure we get a coherent view of the
+ // remote process.
+ // That also means that we *MUST* directly read the memory, which is why we template the make() call
+ withRemoteBuffer(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, true, false, kr, ^(void *buffer, size_t size) {
+ dyld_process_info_ptr base;
+ if (task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+ const dyld_all_image_infos_32* info = (const dyld_all_image_infos_32*)buffer;
+ base = dyld_process_info_base::make<dyld_all_image_infos_32, dyld_image_info_32>(task, *info, timestamp, kr);
+ } else {
+ const dyld_all_image_infos_64* info = (const dyld_all_image_infos_64*)buffer;
+ base = dyld_process_info_base::make<dyld_all_image_infos_64, dyld_image_info_64>(task, *info, timestamp, kr);
}
- if ( allImageInfo64.infoArrayChangeTimestamp != doneTimeStamp ) {
- // image list has changed since we started reading it
- // throw out what we have and start over
- free((void*)result);
- result = nullptr;
+ if (base) {
+ result = base.release();
}
- }
-
- return result;
-}
-
-
-dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr)
-{
- // Other process may be loading and unloading as we read its memory, which can cause a read failure
- // <rdar://problem30067343&29567679> Retry if something fails
- for (int i=0; i < 100; ++i) {
- if ( dyld_process_info result = _dyld_process_info_create_inner( task, timestamp, kr) )
- return result;
-
- }
- return NULL;
+ });
+ return result;
}
void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo)
*cacheInfo = *info->cacheInfo();
}
-void _dyld_process_info_retain(dyld_process_info info)
+void _dyld_process_info_retain(dyld_process_info object)
{
- info->retainCount() += 1;
+ const_cast<dyld_process_info_base*>(object)->retain();
}
-void _dyld_process_info_release(dyld_process_info info)
+void _dyld_process_info_release(dyld_process_info object)
{
- info->retainCount() -= 1;
- if ( info->retainCount() == 0 )
- free((void*)info);
+ const_cast<dyld_process_info_base*>(object)->release();
}
void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path))
#ifndef _DYLD_PROCESS_INFO_INTERNAL_H_
#define _DYLD_PROCESS_INFO_INTERNAL_H_
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
+#include <uuid/uuid.h>
+#include <array>
struct dyld_all_image_infos_32 {
- uint32_t version;
- uint32_t infoArrayCount;
- uint32_t infoArray;
- uint32_t notification;
- bool processDetachedFromSharedRegion;
- bool libSystemInitialized;
- uint32_t dyldImageLoadAddress;
- uint32_t jitInfo;
- uint32_t dyldVersion;
- uint32_t errorMessage;
- uint32_t terminationFlags;
- uint32_t coreSymbolicationShmPage;
- uint32_t systemOrderFlag;
- uint32_t uuidArrayCount;
- uint32_t uuidArray;
- uint32_t dyldAllImageInfosAddress;
- uint32_t initialImageCount;
- uint32_t errorKind;
- uint32_t errorClientOfDylibPath;
- uint32_t errorTargetDylibPath;
- uint32_t errorSymbol;
- uint32_t sharedCacheSlide;
- uint8_t sharedCacheUUID[16];
- uint32_t sharedCacheBaseAddress;
- uint64_t infoArrayChangeTimestamp;
- uint32_t dyldPath;
- uint32_t notifyMachPorts[8];
- uint32_t reserved[5];
- uint32_t compact_dyld_image_info_addr;
- uint32_t compact_dyld_image_info_size;
+ uint32_t version;
+ uint32_t infoArrayCount;
+ std::atomic<uint32_t> infoArray;
+ uint32_t notification;
+ bool processDetachedFromSharedRegion;
+ bool libSystemInitialized;
+ uint32_t dyldImageLoadAddress;
+ uint32_t jitInfo;
+ uint32_t dyldVersion;
+ uint32_t errorMessage;
+ uint32_t terminationFlags;
+ uint32_t coreSymbolicationShmPage;
+ uint32_t systemOrderFlag;
+ uint32_t uuidArrayCount;
+ uint32_t uuidArray;
+ uint32_t dyldAllImageInfosAddress;
+ uint32_t initialImageCount;
+ uint32_t errorKind;
+ uint32_t errorClientOfDylibPath;
+ uint32_t errorTargetDylibPath;
+ uint32_t errorSymbol;
+ uint32_t sharedCacheSlide;
+ std::array<uint8_t, 16> sharedCacheUUID;
+ uint32_t sharedCacheBaseAddress;
+ std::atomic<uint64_t> infoArrayChangeTimestamp;
+ uint32_t dyldPath;
+ 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 {
- uint32_t version;
- uint32_t infoArrayCount;
- uint64_t infoArray;
- uint64_t notification;
- bool processDetachedFromSharedRegion;
- bool libSystemInitialized;
- uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
- uint64_t dyldImageLoadAddress;
- uint64_t jitInfo;
- uint64_t dyldVersion;
- uint64_t errorMessage;
- uint64_t terminationFlags;
- uint64_t coreSymbolicationShmPage;
- uint64_t systemOrderFlag;
- uint64_t uuidArrayCount;
- uint64_t uuidArray;
- uint64_t dyldAllImageInfosAddress;
- uint64_t initialImageCount;
- uint64_t errorKind;
- uint64_t errorClientOfDylibPath;
- uint64_t errorTargetDylibPath;
- uint64_t errorSymbol;
- uint64_t sharedCacheSlide;
- uint8_t sharedCacheUUID[16];
- uint64_t sharedCacheBaseAddress;
- uint64_t infoArrayChangeTimestamp;
- uint64_t dyldPath;
- uint32_t notifyMachPorts[8];
- uint64_t reserved[9];
- uint64_t compact_dyld_image_info_addr;
- uint64_t compact_dyld_image_info_size;
+ uint32_t version;
+ uint32_t infoArrayCount;
+ std::atomic<uint64_t> infoArray;
+ uint64_t notification;
+ bool processDetachedFromSharedRegion;
+ bool libSystemInitialized;
+ uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
+ uint64_t dyldImageLoadAddress;
+ uint64_t jitInfo;
+ uint64_t dyldVersion;
+ uint64_t errorMessage;
+ uint64_t terminationFlags;
+ uint64_t coreSymbolicationShmPage;
+ uint64_t systemOrderFlag;
+ uint64_t uuidArrayCount;
+ uint64_t uuidArray;
+ uint64_t dyldAllImageInfosAddress;
+ uint64_t initialImageCount;
+ uint64_t errorKind;
+ uint64_t errorClientOfDylibPath;
+ uint64_t errorTargetDylibPath;
+ uint64_t errorSymbol;
+ uint64_t sharedCacheSlide;
+ std::array<uint8_t, 16> sharedCacheUUID;
+ uint64_t sharedCacheBaseAddress;
+ std::atomic<uint64_t> infoArrayChangeTimestamp;
+ uint64_t dyldPath;
+ 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 {
uint64_t timestamp;
};
+struct VIS_HIDDEN RemoteBuffer {
+ RemoteBuffer();
+ RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation);
+ ~RemoteBuffer();
+ RemoteBuffer& operator=(RemoteBuffer&& other) {
+ _localAddress = other._localAddress;
+ _size = other._size;
+ _kr = other._kr;
+ other._localAddress = 0;
+ other._size = 0;
+ other._kr = KERN_SUCCESS;
+ return *this;
+ }
+ RemoteBuffer(const RemoteBuffer &) = delete;
+ RemoteBuffer& operator=(const RemoteBuffer &) = delete;
+ void *getLocalAddress();
+ kern_return_t getKernelReturn();
+ size_t getSize();
+private:
+ bool map(task_t task, mach_vm_address_t remote_address, bool shared);
+ mach_vm_address_t _localAddress;
+ vm_size_t _size;
+ kern_return_t _kr;
+};
+// only called during libdyld set up
+void setNotifyMonitoringDyldMain(void (*func)()) VIS_HIDDEN;
+void setNotifyMonitoringDyld(void (*func)(bool unloading, unsigned imageCount,
+ const struct mach_header* loadAddresses[],
+ const char* imagePaths[])) VIS_HIDDEN;
+
+void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) __attribute__((visibility("hidden")));
+
+template<typename T>
+VIS_HIDDEN void withRemoteObject(task_t task, mach_vm_address_t remote_address, bool shared, kern_return_t *kr, void (^block)(T t))
+{
+ withRemoteBuffer(task, remote_address, sizeof(T), shared, false, kr, ^(void *buffer, size_t size) {
+ block(*reinterpret_cast<T *>(buffer));
+ });
+}
#endif // _DYLD_PROCESS_INFO_INTERNAL_H_
#include <mach/shared_region.h>
#include <mach/mach_vm.h>
#include <libkern/OSAtomic.h>
+#include <execinfo.h>
+
#include "dyld_process_info.h"
#include "dyld_process_info_internal.h"
#include "dyld_images.h"
#include "dyld_priv.h"
-#include "LaunchCache.h"
#include "Loading.h"
#include "AllImages.h"
+extern "C" int _dyld_func_lookup(const char* name, void** address);
typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
typedef void (^NotifyExit)();
typedef void (^NotifyMain)();
-
//
// Object used for monitoring another processes dyld loads
//
struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base
{
- static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr);
- ~dyld_process_info_notify_base();
-
- bool incRetainCount() const;
- bool decRetainCount() const;
+ dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task, kern_return_t* kr);
+ ~dyld_process_info_notify_base();
+ bool enabled() const;
+ void retain();
+ void release();
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); }
+ static void operator delete(void* p) { 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();
-
- mutable int32_t _retainCount;
- dispatch_queue_t _queue;
- Notify _notify;
- NotifyExit _notifyExit;
- mutable NotifyMain _notifyMain;
- task_t _targetTask;
- dispatch_source_t _machSource;
- uint64_t _portAddressInTarget;
- mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading
- mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading
+ void handleEvent();
+ void teardown();
+ void replyToMonitoredProcess(mach_msg_header_t& header);
+
+ RemoteBuffer _remoteAllImageInfoBuffer;
+ uint32_t* _notifyMachPorts;
+ uint32_t _notifySlot;
+ mutable std::atomic<int32_t> _retainCount;
+ dispatch_queue_t _queue;
+ Notify _notify;
+ NotifyExit _notifyExit;
+ mutable NotifyMain _notifyMain;
+ task_t _targetTask;
+ dispatch_source_t _machSource;
+ mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading
+ mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading
+ std::atomic<bool> _disabled;
};
-dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task)
- : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _notifyMain(NULL), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0)
+dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit,
+ task_t task, kern_return_t* kr) :
+ _notifyMachPorts(nullptr), _notifySlot(0), _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit),
+ _notifyMain(nullptr), _targetTask(task), _machSource(nullptr), _sendPortInTarget(0), _receivePortInMonitor(0),
+ _disabled(false)
{
+ assert(_disabled == false);
dispatch_retain(_queue);
-}
-
-dyld_process_info_notify_base::~dyld_process_info_notify_base()
-{
- if ( _machSource ) {
- dispatch_source_cancel(_machSource);
- dispatch_release(_machSource);
- _machSource = NULL;
- }
- if ( _portAddressInTarget ) {
- unpokeSendPortInTarget();
- _portAddressInTarget = 0;
- }
- if ( _sendPortInTarget ) {
- _sendPortInTarget = 0;
- }
- dispatch_release(_queue);
- if ( _receivePortInMonitor != 0 ) {
- mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
- _receivePortInMonitor = 0;
- }
-}
-
-bool dyld_process_info_notify_base::incRetainCount() const
-{
- int32_t newCount = OSAtomicIncrement32(&_retainCount);
- return ( newCount == 1 );
-}
-
-bool dyld_process_info_notify_base::decRetainCount() const
-{
- int32_t newCount = OSAtomicDecrement32(&_retainCount);
- return ( newCount == 0 );
-}
+ // Allocate a port to listen on in this monitoring task
+ mach_port_options_t options = { .flags = MPO_IMPORTANCE_RECEIVER | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+ .mpl = { MACH_PORT_QLIMIT_DEFAULT }};
+ if ((*kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_receivePortInMonitor))) {
+ teardown();
+ return;
+ }
+ if (_targetTask == mach_task_self()) {
+ _sendPortInTarget = _receivePortInMonitor;
+ (void)mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+ } else {
+ // Insert a deadname right into the port to trigger notifications
+ kern_return_t r = KERN_NAME_EXISTS;
+ while (r == KERN_NAME_EXISTS) {
+ _sendPortInTarget = MACH_PORT_NULL;
+ //FIXME file radar
+ r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget);
+ if (r != KERN_SUCCESS) {
+ *kr = r;
+ return;
+ }
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+ }
+ if (r != KERN_SUCCESS) {
+ *kr = r;
+ return;
+ }
+ // Notify us if the target dies
+ mach_port_t previous = MACH_PORT_NULL;
+ if ((*kr = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ // This is a new port, if there is a previous notifier attached then something is wrong... abort.
+ if (previous != MACH_PORT_NULL) {
+ (void)mach_port_deallocate(mach_task_self(), previous);
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ }
-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)
-{
- dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
+ // Setup the event handler for the port
+ _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
+ if (_machSource == nullptr) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ teardown();
+ return;
+ }
+ dispatch_source_set_event_handler(_machSource, ^{
+ handleEvent();
+ });
+ dispatch_source_set_cancel_handler(_machSource, ^{
+ if ( _receivePortInMonitor != 0 ) {
+ (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+ _receivePortInMonitor = 0;
+ }
+ });
+ dispatch_activate(_machSource);
- if ( kern_return_t r = obj->makePorts() ) {
- if ( kr != NULL )
- *kr = r;
- goto fail;
- }
+ // get location on all_image_infos in the target task
+ task_dyld_info_data_t taskDyldInfo;
+ mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT;
+ if ((*kr = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount))) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ return;
+ }
+ // Poke the portname of our port into the target task
+ _remoteAllImageInfoBuffer = RemoteBuffer(_targetTask, taskDyldInfo.all_image_info_addr, taskDyldInfo.all_image_info_size, true, false);
+ *kr = _remoteAllImageInfoBuffer.getKernelReturn();
+ if (*kr) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ return;
+ }
- obj->setMachSourceOnQueue();
+ static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
+ if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+ _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_32,notifyMachPorts));
+ } else {
+ _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_64,notifyMachPorts));
+ }
- if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) {
- if ( kr != NULL )
- *kr = r;
- goto fail;
- }
+#if 0
+ //If all the slots are filled we will sleep and retry a few times before giving up
+ for (uint32_t i=0; i<10; ++i) {
+ for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+ if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ break;
+ }
+ }
+ if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ // all the slots are filled, sleep and try again
+ usleep(1000 * 50); // 50ms
+ } else {
+ // if _notifySlot is set we are done
+ break;
+ }
+ }
+#else
+ for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+ if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ break;
+ }
+ }
+#endif
- if ( kr != NULL )
- *kr = KERN_SUCCESS;
- return obj;
+ if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ teardown();
+ *kr = KERN_UREFS_OVERFLOW;
+ return;
+ }
-fail:
- delete obj;
- return NULL;
+ *kr = KERN_SUCCESS;
}
-
-kern_return_t dyld_process_info_notify_base::makePorts()
-{
- // Allocate a port to listen on in this monitoring task
- if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) )
- return r;
-
- // Add send rights for replying
- if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
- return r;
-
- // Allocate a name in the target. We need a new name to add send rights to
- if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) )
- return r;
-
- // Deallocate the dead name
- if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) )
- return r;
-
- // Make the dead name a send right to our listening port
- if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
- return r;
-
- // Notify us if the target dies
- mach_port_t previous = MACH_PORT_NULL;
- if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))
- return r;
-
- //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor);
- return KERN_SUCCESS;
+dyld_process_info_notify_base::~dyld_process_info_notify_base() {
+ if (!_disabled) {
+ fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still enabled\n");
+ }
+ dispatch_release(_queue);
}
-
-
-void dyld_process_info_notify_base::setMachSourceOnQueue()
-{
- NotifyExit exitHandler = _notifyExit;
- _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
- dispatch_source_set_event_handler(_machSource, ^{
- // This event handler block has an implicit reference to "this"
- // if incrementing the count goes to one, that means the object may have already been destroyed
- if ( incRetainCount() )
- return;
- uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE];
- mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
-
- kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
- if ( r == KERN_SUCCESS ) {
- //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
- if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
- // run notifier block for each [un]load image
- const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
- const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
- const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
- for (unsigned i=0; i < header->imageCount; ++i) {
- bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
- _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
- }
- // reply to dyld, so it can continue
- mach_msg_header_t replyHeader;
- replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
- replyHeader.msgh_id = 0;
- replyHeader.msgh_local_port = MACH_PORT_NULL;
- replyHeader.msgh_remote_port = h->msgh_remote_port;
- replyHeader.msgh_reserved = 0;
- replyHeader.msgh_size = sizeof(replyHeader);
- mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
- }
- else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
- if ( _notifyMain != NULL ) {
- _notifyMain();
- }
- // reply to dyld, so it can continue
- mach_msg_header_t replyHeader;
- replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
- replyHeader.msgh_id = 0;
- replyHeader.msgh_local_port = MACH_PORT_NULL;
- replyHeader.msgh_remote_port = h->msgh_remote_port;
- replyHeader.msgh_reserved = 0;
- replyHeader.msgh_size = sizeof(replyHeader);
- mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
- }
- else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
- mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
- //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort);
- if ( deadPort == _sendPortInTarget ) {
- // target process died. Clean up ports
- _sendPortInTarget = 0;
- mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
- _receivePortInMonitor = 0;
- _portAddressInTarget = 0;
- // notify that target is gone
- exitHandler();
- }
- }
- else {
- fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
- }
+void dyld_process_info_notify_base::teardown() {
+ if (!_disabled) {
+ _disabled = true;
+ // The connection to the target is dead. Clean up ports
+ if ( _remoteAllImageInfoBuffer.getLocalAddress() != 0 && _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+ mach_port_t extractedPort = MACH_PORT_NULL;
+ mach_msg_type_name_t extractedPortType;
+ kern_return_t kr = mach_port_extract_right(_targetTask, _sendPortInTarget, MACH_MSG_TYPE_COPY_SEND, &extractedPort, &extractedPortType);
+ if (kr == KERN_SUCCESS) {
+ if (extractedPort == _receivePortInMonitor) {
+ if (OSAtomicCompareAndSwap32(_sendPortInTarget, 0, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+ (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+ }
+ }
+ (void)mach_port_deallocate(mach_task_self(), extractedPort);
+ }
}
- if ( decRetainCount() )
- delete this;
- });
- dispatch_resume(_machSource);
+ _sendPortInTarget = 0;
+ if ( _machSource ) {
+ dispatch_source_cancel(_machSource);
+ dispatch_release(_machSource);
+ _machSource = NULL;
+ }
+ if (_notifyExit) {
+ dispatch_async(_queue, ^{
+ _notifyExit();
+ });
+ }
+ }
}
-
-kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget()
+bool dyld_process_info_notify_base::enabled() const
{
- // get location on all_image_infos in target task
- task_dyld_info_data_t taskDyldInfo;
- mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
- kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count);
- if ( r )
- return r;
-
- // remap the page containing all_image_infos into this process r/w
- mach_vm_address_t mappedAddress = 0;
- mach_vm_size_t mappedSize = taskDyldInfo.all_image_info_size;
- vm_prot_t curProt = VM_PROT_NONE;
- vm_prot_t maxProt = VM_PROT_NONE;
- r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
- _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE);
- if ( r )
- return r;
- if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
- return KERN_PROTECTION_FAILURE;
-
- // atomically set port into all_image_info_struct
- static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
-
- mach_vm_address_t mappedAddressToPokePort = 0;
- if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 )
- mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts);
- else
- mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts);
-
- // use first available slot
- bool slotFound = false;
- for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) {
- if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) {
- slotFound = true;
- break;
- }
- mappedAddressToPokePort += sizeof(uint32_t);
- }
- if ( !slotFound ) {
- mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
- return KERN_UREFS_OVERFLOW;
- }
- _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress;
- //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget);
- mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
- return r;
+ return !_disabled;
}
+void dyld_process_info_notify_base::retain()
+{
+ _retainCount++;
+}
-
-kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget()
+void dyld_process_info_notify_base::release()
{
- // remap the page containing all_image_infos into this process r/w
- mach_vm_address_t mappedAddress = 0;
- mach_vm_size_t mappedSize = sizeof(mach_port_t);
- vm_prot_t curProt = VM_PROT_NONE;
- vm_prot_t maxProt = VM_PROT_NONE;
- kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
- _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE);
- if ( r )
- return r;
- if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
- return KERN_PROTECTION_FAILURE;
-
- OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress);
-
- //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget);
- mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
- return r;
+ uint32_t newCount = --_retainCount;
+
+ if ( newCount == 0 ) {
+ teardown();
+ }
+ dispatch_async(_queue, ^{
+ delete this;
+ });
}
+void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& header) {
+ mach_msg_header_t replyHeader;
+ replyHeader.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSGH_BITS_REMOTE(header.msgh_bits), 0, 0, 0);
+ replyHeader.msgh_id = 0;
+ replyHeader.msgh_local_port = MACH_PORT_NULL;
+ replyHeader.msgh_remote_port = header.msgh_remote_port;
+ replyHeader.msgh_reserved = 0;
+ replyHeader.msgh_size = sizeof(replyHeader);
+ kern_return_t r = mach_msg(&replyHeader, MACH_SEND_MSG, replyHeader.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+ if (r == KERN_SUCCESS) {
+ header.msgh_remote_port = MACH_PORT_NULL;
+ } else {
+ teardown();
+ }
+}
+void dyld_process_info_notify_base::handleEvent() {
+ // References object may still exist even after the ports are dead. Disable event dispatching
+ // if the ports have been torn down.
+ if (_disabled) {
+ return;
+ }
+ // This event handler block has an implicit reference to "this"
+ // if incrementing the count goes to one, that means the object may have already been destroyed
+ uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE] = {};
+ mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+
+ kern_return_t r = mach_msg(h, MACH_RCV_MSG | MACH_RCV_VOUCHER| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0), 0, sizeof(messageBuffer)-sizeof(mach_msg_audit_trailer_t), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if ( r == KERN_SUCCESS && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
+ //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+
+ if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
+ // run notifier block for each [un]load image
+ const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
+ if (sizeof(*header) <= h->msgh_size
+ && header->imagesOffset <= h->msgh_size
+ && header->stringsOffset <= h->msgh_size
+ && (header->imageCount * sizeof(dyld_process_info_image_entry)) <= (h->msgh_size - header->imagesOffset)) {
+ const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
+ const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
+ for (unsigned i=0; i < header->imageCount; ++i) {
+ bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
+ if (entries[i].pathStringOffset <= h->msgh_size - header->stringsOffset) {
+ //fprintf(stderr, "Notifying about: %s\n", stringPool + entries[i].pathStringOffset);
+ _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
+ } else {
+ teardown();
+ break;
+ }
+ }
+ // reply to dyld, so it can continue
+ replyToMonitoredProcess(*h);
+ } else {
+ teardown();
+ }
+ }
+ else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
+ if (h->msgh_size != sizeof(mach_msg_header_t)) {
+ teardown();
+ } else if ( _notifyMain != NULL ) {
+ _notifyMain();
+ }
+ replyToMonitoredProcess(*h);
+ } else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
+ mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
+ // Validate this notification came from the kernel
+ const mach_msg_audit_trailer_t *audit_tlr = (mach_msg_audit_trailer_t *)((uint8_t *)h + round_msg(h->msgh_size));
+ if (audit_tlr->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0
+ && audit_tlr->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)
+ // We cannot link to libbsm, so we are hardcoding the audit token offset (5)
+ // And the value the represents the kernel (0)
+ && audit_tlr->msgh_audit.val[5] == 0
+ && deadPort == _sendPortInTarget ) {
+ teardown();
+ }
+ }
+ else {
+ fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+ }
+ }
+ mach_msg_destroy(h);
+}
dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
void (^notifyExit)(),
kern_return_t* kr)
{
- return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr);
+ kern_return_t krSink = KERN_SUCCESS;
+ if (kr == nullptr) {
+ kr = &krSink;
+ }
+ *kr = KERN_SUCCESS;
+
+ dyld_process_info_notify result = new dyld_process_info_notify_base(queue, notify, notifyExit, task, kr);
+ if (result->enabled())
+ return result;
+ const_cast<dyld_process_info_notify_base*>(result)->release();
+ return nullptr;
}
void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^notifyMain)())
void _dyld_process_info_notify_retain(dyld_process_info_notify object)
{
- object->incRetainCount();
+ const_cast<dyld_process_info_notify_base*>(object)->retain();
}
void _dyld_process_info_notify_release(dyld_process_info_notify object)
{
- // Note if _machSource is currently handling a message, the retain count will not be zero
- // and object will instead be deleted when handling is done.
- if ( object->decRetainCount() )
- delete object;
+ const_cast<dyld_process_info_notify_base*>(object)->release();
}
+static void (*sNotifyMonitoringDyldMain)() = nullptr;
+static void (*sNotifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+ const char* imagePaths[]) = nullptr;
-
-
-
-
-
-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)
+void setNotifyMonitoringDyldMain(void (*func)())
{
- if ( sZombieNotifiers[portSlot] )
- return;
+ sNotifyMonitoringDyldMain = func;
+}
- 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 setNotifyMonitoringDyld(void (*func)(bool unloading, unsigned imageCount,
+ const struct mach_header* loadAddresses[],
+ const char* imagePaths[]))
+{
+ sNotifyMonitoringDyld = func;
}
+namespace dyld3 {
+
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;
- }
- }
- }
+ assert(sNotifyMonitoringDyldMain != nullptr);
+ sNotifyMonitoringDyldMain();
}
-void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+void AllImages::notifyMonitorLoads(const Array<LoadedImage>& 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;
- }
+ assert(sNotifyMonitoringDyld != nullptr);
+ const struct mach_header* loadAddresses[newImages.count()];
+ const char* loadPaths[newImages.count()];
+ for(uint32_t i = 0; i<newImages.count(); ++i) {
+ loadAddresses[i] = newImages[i].loadedAddress();
+ loadPaths[i] = newImages[i].image()->path();
}
+ sNotifyMonitoringDyld(false, (unsigned)newImages.count(), loadAddresses, loadPaths);
}
-void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
+void AllImages::notifyMonitorUnloads(const Array<LoadedImage>& 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;
- }
+ assert(sNotifyMonitoringDyld != nullptr);
+ const struct mach_header* loadAddresses[unloadingImages.count()];
+ const char* loadPaths[unloadingImages.count()];
+ for(uint32_t i = 0; i<unloadingImages.count(); ++i) {
+ loadAddresses[i] = unloadingImages[i].loadedAddress();
+ loadPaths[i] = unloadingImages[i].image()->path();
}
+ sNotifyMonitoringDyld(true, (unsigned)unloadingImages.count(), loadAddresses, loadPaths);
}
} // namespace dyld3
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
+ * Reserved. 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 1.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.apple.com/publicsource 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 OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <map>
+#include <set>
+#include <memory>
+#include <string>
+#include <cstring>
+#include <array>
+#include <vector>
+#include <iomanip>
+#include <sstream>
+#include <iostream>
+#include <cinttypes>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <libutil.h>
+#include <ktrace/session.h>
+#include <dispatch/dispatch.h>
+#include <System/sys/kdebug.h>
+
+#include "Tracing.h"
+
+#define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
+
+
+/*
+ * MAXCOLS controls when extra data kicks in.
+ * MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path.
+ */
+#define MAXCOLS (132)
+#define MAXWIDTH (MAXCOLS + 64)
+
+unsigned int columns = 0;
+ktrace_session_t s;
+bool wideflag = false;
+bool RAW_flag = false;
+bool JSON_flag = false;
+bool JSON_Tracing_flag = false;
+dispatch_source_t sigwinch_source;
+static void
+exit_usage(void)
+{
+ fprintf(stderr, "Usage: dyld_usage [-e] [-f mode] [-t seconds] [-R rawfile [-S start_time] [-E end_time]] [pid | cmd [pid | cmd] ...]\n");
+ fprintf(stderr, " -e exclude the specified list of pids from the sample\n");
+ fprintf(stderr, " and exclude dyld_usage by default\n");
+ fprintf(stderr, " -t specifies timeout in seconds (for use in automated tools)\n");
+ fprintf(stderr, " -R specifies a raw trace file to process\n");
+ fprintf(stderr, " pid selects process(s) to sample\n");
+ fprintf(stderr, " cmd selects process(s) matching command string to sample\n");
+ fprintf(stderr, "By default (no options) the following processes are excluded from the output:\n");
+ fprintf(stderr, "dyld_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n\n");
+
+ exit(1);
+}
+
+static void
+get_screenwidth(void)
+{
+ struct winsize size;
+
+ columns = MAXCOLS;
+
+ if (isatty(STDOUT_FILENO)) {
+ if (ioctl(1, TIOCGWINSZ, &size) != -1) {
+ columns = size.ws_col;
+
+ if (columns > MAXWIDTH)
+ columns = MAXWIDTH;
+ }
+ }
+}
+
+std::map<uint64_t, uint64_t> gActiveStringIDs;
+std::map<uint64_t, std::string> gActiveStrings;
+
+const std::string& stringForID(uint64_t id) {
+ static std::string emptyString = "";
+ auto i = gActiveStrings.find(id);
+ if (i == gActiveStrings.end())
+ return emptyString;
+ return i->second;
+}
+
+static uint64_t
+mach_to_nano(uint64_t mach)
+{
+ uint64_t nanoseconds = 0;
+ assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0);
+ return nanoseconds;
+}
+
+struct output_renderer {
+ output_renderer(ktrace_session_t S, ktrace_event_t E) :
+ _commandName(ktrace_get_execname_for_thread(s, E->threadid)),
+ _threadid(E->threadid), _pid(ktrace_get_pid_for_thread(s, E->threadid)) {}
+ void recordEvent(ktrace_event_t event) {
+ uint32_t code = event->debugid & KDBG_EVENTID_MASK;
+ if (event->debugid & DBG_FUNC_START) {
+ switch(code) {
+ case DBG_DYLD_TIMING_DLOPEN: enqueueEvent<dlopen>(event, true); break;
+ case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE: enqueueEvent<app_launch>(event, true); break;
+ case DBG_DYLD_TIMING_DLSYM: enqueueEvent<dlsym>(event, true); break;
+ case DBG_DYLD_TIMING_STATIC_INITIALIZER: enqueueEvent<static_init>(event, false); break;
+ case DBG_DYLD_TIMING_MAP_IMAGE: enqueueEvent<map_image>(event, false); break;
+ case DBG_DYLD_TIMING_APPLY_FIXUPS: enqueueEvent<apply_fixups>(event, false); break;
+ case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE: enqueueEvent<attach_signature>(event, false); break;
+ case DBG_DYLD_TIMING_BUILD_CLOSURE: enqueueEvent<build_closure>(event, false); break;
+ case DBG_DYLD_TIMING_DLADDR: enqueueEvent<dladdr>(event, true); break;
+ case DBG_DYLD_TIMING_DLCLOSE: enqueueEvent<dlclose>(event, true); break;
+ case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE: enqueueEvent<add_image_callback>(event, false); break;
+ case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE: enqueueEvent<remove_image_callback>(event, false); break;
+ case DBG_DYLD_TIMING_OBJC_INIT: enqueueEvent<objc_image_init>(event, false); break;
+ case DBG_DYLD_TIMING_OBJC_MAP: enqueueEvent<objc_images_map>(event, false); break;
+ }
+ } else {
+ switch(code) {
+ case DBG_DYLD_TIMING_DLOPEN: dequeueEvent<dlopen>(event, [&](dlopen* endEvent){
+ endEvent->result = event->arg2;
+ }); break;
+ case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE: dequeueEvent<app_launch>(event, [&](app_launch* endEvent){
+ endEvent->launchMode = event->arg4;
+ }); break;
+ case DBG_DYLD_TIMING_DLSYM: dequeueEvent<dlsym>(event, [&](dlsym* endEvent){
+ endEvent->result = event->arg2;
+ }); break;
+ case DBG_DYLD_TIMING_STATIC_INITIALIZER: dequeueEvent<static_init>(event, [&](static_init* endEvent){}); break;
+ case DBG_DYLD_TIMING_MAP_IMAGE: dequeueEvent<map_image>(event, [&](map_image* endEvent){
+ endEvent->result = event->arg2;
+ }); break;
+ case DBG_DYLD_TIMING_APPLY_FIXUPS: dequeueEvent<apply_fixups>(event, [&](apply_fixups* endEvent){}); break;
+ case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE: dequeueEvent<attach_signature>(event, [&](attach_signature* endEvent){}); break;
+ case DBG_DYLD_TIMING_BUILD_CLOSURE: dequeueEvent<build_closure>(event, [&](build_closure* endEvent){}); break;
+ case DBG_DYLD_TIMING_DLCLOSE: dequeueEvent<dlclose>(event, [&](dlclose* endEvent){
+ endEvent->result = (int)event->arg2;
+ }); break;
+ case DBG_DYLD_TIMING_DLADDR: dequeueEvent<dladdr>(event, [&](dladdr* endEvent){
+ endEvent->result = (int)event->arg2;
+ endEvent->imageAddress = (int)event->arg3;
+ endEvent->symbolAddress = (int)event->arg4;
+ }); break;
+ case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE: dequeueEvent<add_image_callback>(event, [&](add_image_callback* endEvent){}); break;
+ case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE: dequeueEvent<remove_image_callback>(event, [&](remove_image_callback* endEvent){}); break;
+ case DBG_DYLD_TIMING_OBJC_INIT: dequeueEvent<objc_image_init>(event, [&](objc_image_init* endEvent){}); break;
+ case DBG_DYLD_TIMING_OBJC_MAP: dequeueEvent<objc_images_map>(event, [&](objc_images_map* endEvent){}); break;
+ }
+ }
+ }
+
+ bool empty() { return !_currentRootEvent && _eventStack.empty() && _rootEvents.empty(); }
+private:
+ template<typename T>
+ void enqueueEvent(ktrace_event_t event, bool rootEvent) {
+ auto sharedEvent = std::make_shared<T>(event);
+ if (!_currentRootEvent) {
+ if (!rootEvent) return;
+ assert(_eventStack.empty());
+ _currentRootEvent = sharedEvent;
+ } else {
+ sharedEvent->setDepth(_eventStack.size());
+ _eventStack.back()->addChild(sharedEvent);
+ }
+ _eventStack.push_back(sharedEvent);
+ }
+
+ template<typename T>
+ void dequeueEvent(ktrace_event_t event, std::function<void(T*)> lambda) {
+ if (_eventStack.empty()) return;
+ auto currentEvent = dynamic_cast<T*>(_eventStack.back().get());
+ currentEvent->setEndEvent(event);
+ lambda(currentEvent);
+ _eventStack.pop_back();
+ if (_currentRootEvent && _eventStack.empty()) {
+ output(_currentRootEvent);
+ _currentRootEvent = nullptr;
+ }
+ }
+
+ struct event_pair {
+ event_pair(ktrace_event_t E) : _startTime(E->timestamp), _walltime(E->walltime), _threadid(E->threadid), _depth(0),
+ _eventCode(KDBG_EXTRACT_CODE(E->debugid)) {};
+ virtual ~event_pair(){}
+ std::vector<std::shared_ptr<event_pair>>& children() { return _children; }
+ void setDepth(uint64_t D) { _depth = D; }
+ uint64_t depth() { return _depth; }
+ struct timeval walltime() { return _walltime; };
+ uint64_t startTimestamp() { return _startTime; };
+ uint64_t endTimestamp() { return _endTime; }
+ unsigned long threadid() { return _threadid; }
+ void addChild(std::shared_ptr<event_pair> child) {_children.push_back(child);}
+ void setEndEvent(ktrace_event_t E) { _endTime = E->timestamp; }
+ uint16_t eventCode() const { return _eventCode; }
+ private:
+ std::vector<std::shared_ptr<event_pair>> _children;
+ uint64_t _startTime;
+ uint64_t _endTime;
+ uint64_t _depth;
+ unsigned long _threadid;
+ uint16_t _eventCode;
+ struct timeval _walltime;
+ };
+
+ time_t _lastTimeWallSeconds = -1;
+ std::string _timestampStr;
+ std::string _commandName;
+ pid_t _pid;
+ unsigned long _threadid;
+
+ std::string timestamp(std::shared_ptr<event_pair> event, bool extended) {
+ std::ostringstream result;
+ struct timeval now_walltime = event->walltime();
+ assert(now_walltime.tv_sec || now_walltime.tv_usec);
+
+ /* try and reuse the timestamp string */
+ if (_lastTimeWallSeconds != now_walltime.tv_sec) {
+ char timestamp[32];
+ (void)strftime(timestamp, sizeof (timestamp), "%H:%M:%S", localtime(&now_walltime.tv_sec));
+ _lastTimeWallSeconds = now_walltime.tv_sec;
+ _timestampStr = timestamp;
+ }
+ result << _timestampStr;
+
+ if (extended) {
+ result << "." << std::setw(6) << std::setfill(' ') << std::to_string(now_walltime.tv_usec);
+ }
+ return result.str();
+ }
+
+ std::string process(std::shared_ptr<event_pair> event, bool extended) {
+ if (extended) {
+ std::ostringstream result;
+ result << _commandName << "." << std::to_string(event->threadid());
+ return result.str();
+ } else {
+ std::string result = _commandName;
+ result.resize(12, ' ');
+ return result;
+ }
+ }
+
+ std::string duration(std::shared_ptr<event_pair> event) {
+ std::ostringstream result;
+ uint64_t usecs = (mach_to_nano(event->endTimestamp() - event->startTimestamp()) + (NSEC_PER_USEC - 1)) / NSEC_PER_USEC;
+ uint64_t secs = usecs / USEC_PER_SEC;
+ usecs -= secs * USEC_PER_SEC;
+ result << secs << "." << std::setw(6) << std::setfill('0') << usecs;
+ return result.str();
+ }
+
+public:
+ void outputConsole(std::shared_ptr<event_pair> node, uint64_t width, std::ostringstream& sstr, uint64_t depth) {
+ std::ostringstream line;
+ bool extended = false;
+ if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+ line << "dlopen(\"" << dlopenNode->path << "\", " << dlopenNode->flagString() << ") -> 0x" << dlopenNode->result;
+ } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+ line << "dlsym(0x" << std::hex << dlsymNode->handle << ", \"" << dlsymNode->symbol << "\") -> " << dlsymNode->result;
+ } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+ line << "map file \"" << mapImageNode->path << "\" -> 0x" << std::hex << mapImageNode->result;
+ } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+ line << "attach codesignature";
+ } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+ line << "build closure";
+ } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+ line << "app launch (dyld" << std::dec << launchNode->launchMode << ") -> 0x" << std::hex << launchNode->address;
+ } else if (auto initNode = dynamic_cast<static_init *>(node.get())) {
+ line << "run static initializer 0x" << std::hex << initNode->funcAddress;
+ } else if (auto initNode = dynamic_cast<apply_fixups *>(node.get())) {
+ line << "apply fixups";
+ } else if (auto dlcloseNode = dynamic_cast<dlclose *>(node.get())) {
+ line << "dlclose(0x" << std::hex << dlcloseNode->handle << ") -> " << dlcloseNode->result;
+ } else if (auto dladdrNode = dynamic_cast<dladdr *>(node.get())) {
+ line << "dladdr(0x" << dladdrNode->address << ") -> image: 0x" << std::hex << dladdrNode->imageAddress;
+ line << ", symbol: 0x" << dladdrNode->symbolAddress;
+ } else if (auto addImageNode = dynamic_cast<add_image_callback *>(node.get())) {
+ line << std::hex << "add image callback(0x" << addImageNode->funcAddress << ") for image 0x" << addImageNode->libraryAddress;
+ } else if (auto removeImageNode = dynamic_cast<remove_image_callback *>(node.get())) {
+ line << std::hex << "remove image callback(0x" << removeImageNode->funcAddress << ") for image 0x" << removeImageNode->libraryAddress;
+ } else if (auto objcInitNode = dynamic_cast<objc_image_init *>(node.get())) {
+ line << std::hex << "objC init image(0x" << objcInitNode->libraryAddress << ")";
+ } else if (auto objcMapNode = dynamic_cast<objc_images_map *>(node.get())) {
+ line << std::hex << "objC map images callback";
+ }
+
+ if (width > MAXCOLS) {
+ extended = true;
+ }
+
+ std::string timestampStr = timestamp(node, extended);
+ std::string lineStr = line.str();
+ std::string commandStr = process(node, extended);
+ std::string durationStr = duration(node);
+ uint64_t lineMax = width - (timestampStr.length() + commandStr.length() + durationStr.length() + 2*depth + 3);
+ lineStr.resize(lineMax, ' ');
+
+ sstr << timestampStr << " ";
+ std::fill_n(std::ostream_iterator<char>(sstr), 2*depth, ' ');
+ sstr << lineStr << " " << durationStr << " " << commandStr << std::endl;
+
+ for (const auto& child : node->children()) {
+ outputConsole(child, width, sstr, depth+1);
+ }
+ }
+
+ void outputJSON(std::shared_ptr<event_pair> node, std::ostringstream& sstr) {
+ if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+ sstr << std::hex;
+ sstr << "{\"type\":\"dlopen\",\"path\":\"" << dlopenNode->path << "\",\"flags\":\"0x" << dlopenNode->flags << "\"";
+ sstr << ",\"result\":\"" << dlopenNode->result << "\"";
+ } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+ sstr << std::hex << "{\"type\":\"dlsym\",\"symbol\":\"" << dlsymNode->symbol << "\",\"handle\":\"0x";
+ sstr << dlsymNode->handle << "\",\"result\":\"0x" << dlsymNode->result << "\"";
+ } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+ sstr << std::hex;
+ sstr << "{\"type\":\"map_image\",\"path\":\"" << mapImageNode->path << "\",\"result\":\"0x" << mapImageNode->result << "\"";
+ } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+ sstr << "{\"type\":\"attach_codesignature\"";
+ } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+ sstr << "{\"type\":\"build_closure\"";
+ } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+ sstr << std::hex;
+ sstr << "{\"type\":\"app_launch\",\"address\":\"0x";
+ sstr << launchNode->address << "\",\"mode\":" << launchNode->launchMode << "";
+ } else if (auto initNode = dynamic_cast<static_init *>(node.get())) {
+ sstr << std::hex;
+ sstr << "{\"type\":\"static_init\",\"image_address\":\"0x" << initNode->libraryAddress;
+ sstr << "\",\"function_address\":\"0x" << initNode->funcAddress << "\"";
+ } else if (auto initNode = dynamic_cast<apply_fixups *>(node.get())) {
+ sstr << "{\"type\":\"apply_fixups\"";
+ } else if (auto dlcloseNode = dynamic_cast<dlclose *>(node.get())) {
+ sstr << std::hex << "{\"type\":\"dlclose\",\"handle\":\"0x";
+ sstr << dlcloseNode->handle << "\",\"result\":\"0x" << dlcloseNode->result << "\"";
+ } else if (auto dladdrNode = dynamic_cast<dladdr *>(node.get())) {
+ sstr << std::hex << "{\"type\":\"dladdr\",\"address\":\"0x" << dladdrNode->address << "\",\"result\":\"0x";
+ sstr << dladdrNode->result << "\",\"symbol_address\":\"0x" << dladdrNode->symbolAddress;
+ sstr << "\",\"image_address\":\"0x" << dladdrNode->imageAddress << "\"";
+ } else {
+ sstr << "{\"type\":\"unknown\"";
+ }
+
+ if (!node->children().empty()) {
+ bool firstChild = true;
+ sstr << ",\"children\":[";
+ for (const auto& child : node->children()) {
+ if (!firstChild) {
+ sstr << ",";
+ }
+ firstChild = false;
+ outputJSON(child, sstr);
+ }
+ sstr << "]";
+ }
+ sstr << std::dec << ",\"start_nano\":\"" << mach_to_nano(node->startTimestamp());
+ sstr << "\",\"end_nano\":\"" << mach_to_nano(node->endTimestamp()) << "\"}";
+ }
+
+ void outputTracingJSON(std::shared_ptr<event_pair> node, std::ostringstream& sstr) {
+ auto emitEventInfo = [&](bool isStart) {
+ if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+ sstr << "{\"name\": \"dlopen(" << dlopenNode->path << ")\", \"cat\": \"" << "dlopen" << "\"";
+ } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+ sstr << "{\"name\": \"dlsym(" << dlsymNode->symbol << ")\", \"cat\": \"" << "dlsym" << "\"";
+ } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+ sstr << "{\"name\": \"map_image(" << mapImageNode->path << ")\", \"cat\": \"" << "map_image" << "\"";
+ } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+ sstr << "{\"name\": \"" << "attach_codesignature" << "\", \"cat\": \"" << "attach_codesignature" << "\"";
+ } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+ sstr << "{\"name\": \"" << "build_closure" << "\", \"cat\": \"" << "build_closure" << "\"";
+ } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+ sstr << "{\"name\": \"" << "app_launch" << "\", \"cat\": \"" << "app_launch" << "\"";
+ } else if (auto initNode = dynamic_cast<static_init *>(node.get())) {
+ sstr << "{\"name\": \"" << "static_init" << "\", \"cat\": \"" << "static_init" << "\"";
+ } else if (auto initNode = dynamic_cast<apply_fixups *>(node.get())) {
+ sstr << "{\"name\": \"" << "apply_fixups" << "\", \"cat\": \"" << "apply_fixups" << "\"";
+ } else if (auto dlcloseNode = dynamic_cast<dlclose *>(node.get())) {
+ sstr << "{\"name\": \"" << "dlclose" << "\", \"cat\": \"" << "dlclose" << "\"";
+ } else if (auto dladdrNode = dynamic_cast<dladdr *>(node.get())) {
+ sstr << "{\"name\": \"" << "dladdr" << "\", \"cat\": \"" << "dladdr" << "\"";
+ } else if (auto addImageNode = dynamic_cast<add_image_callback *>(node.get())) {
+ sstr << "{\"name\": \"" << "add_image" << "\", \"cat\": \"" << "add_image" << "\"";
+ } else if (auto removeImageNode = dynamic_cast<remove_image_callback *>(node.get())) {
+ sstr << "{\"name\": \"" << "remove_image" << "\", \"cat\": \"" << "remove_image" << "\"";
+ } else if (auto objcInitNode = dynamic_cast<objc_image_init *>(node.get())) {
+ sstr << "{\"name\": \"" << "objc_init" << "\", \"cat\": \"" << "objc_init" << "\"";
+ } else if (auto objcMapNode = dynamic_cast<objc_images_map *>(node.get())) {
+ sstr << "{\"name\": \"" << "objc_map" << "\", \"cat\": \"" << "objc_map" << "\"";
+ } else {
+ sstr << "{\"name\": \"" << "unknown" << "\", \"cat\": \"" << node->eventCode() << "\"";
+ }
+ if (isStart) {
+ sstr << ", \"ph\": \"B\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->startTimestamp()) << "},";
+ } else {
+ sstr << ", \"ph\": \"E\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->endTimestamp()) << "}";
+ }
+ };
+
+ emitEventInfo(true);
+ emitEventInfo(false);
+
+ if (!node->children().empty()) {
+ for (const auto& child : node->children()) {
+ sstr << ", ";
+ outputTracingJSON(child, sstr);
+ }
+ }
+ }
+
+ const std::vector<std::shared_ptr<event_pair>>& rootEvents() const { return _rootEvents; }
+
+private:
+
+ void output(std::shared_ptr<event_pair> root) {
+ std::ostringstream ostream;
+ if (JSON_flag) {
+ ostream << "{\"command\":\"" << _commandName << "\",\"pid\":\"" << _pid << "\",\"thread\":\"";
+ ostream << _threadid << "\", \"event\":";
+ outputJSON(root, ostream);
+ ostream << "}" << std::endl;
+ } else if (JSON_Tracing_flag) {
+ _rootEvents.push_back(root);
+ } else {
+ outputConsole(root, columns, ostream, 0);
+ }
+ std::cout << ostream.str();
+ if (!RAW_flag)
+ fflush(stdout);
+ }
+
+ struct dlopen : event_pair {
+ dlopen(ktrace_event_t E) : event_pair(E), path(stringForID(E->arg2)), flags((int)E->arg3) {}
+ std::string flagString() {
+ std::vector<std::string> flagStrs;
+ uint64_t flagCheck = 0;
+ std::string flagString;
+
+ if (flags & RTLD_LAZY) {
+ flagStrs.push_back("RTLD_LAZY");
+ flagCheck |= RTLD_LAZY;
+ }
+ if (flags & RTLD_NOW) {
+ flagStrs.push_back("RTLD_NOW");
+ flagCheck |= RTLD_NOW;
+ }
+ if (flags & RTLD_LOCAL) {
+ flagStrs.push_back("RTLD_LOCAL");
+ flagCheck |= RTLD_LOCAL;
+ }
+ if (flags & RTLD_GLOBAL) {
+ flagStrs.push_back("RTLD_GLOBAL");
+ flagCheck |= RTLD_GLOBAL;
+ }
+ if (flags & RTLD_NOLOAD) {
+ flagStrs.push_back("RTLD_NOLOAD");
+ flagCheck |= RTLD_NOLOAD;
+ }
+ if (flags & RTLD_NODELETE) {
+ flagStrs.push_back("RTLD_NODELETE");
+ flagCheck |= RTLD_NODELETE;
+ }
+ if (flags & RTLD_FIRST) {
+ flagStrs.push_back("RTLD_FIRST");
+ flagCheck |= RTLD_FIRST;
+ }
+
+ if (flagCheck == flags) {
+ for (auto& flagStr : flagStrs) {
+ if (!flagString.empty()) {
+ flagString += "|";
+ }
+ flagString += flagStr;
+ }
+ }
+
+ return flagString;
+ }
+ std::string path;
+ int flags;
+ uint64_t result;
+ };
+
+ struct dlsym : event_pair {
+ dlsym(ktrace_event_t E) : event_pair(E), handle(E->arg2), symbol(stringForID(E->arg3)) {}
+ std::string symbol;
+ uint64_t handle;
+ uint64_t result;
+ };
+
+ struct dladdr : event_pair {
+ dladdr(ktrace_event_t E) : event_pair(E), address(E->arg2), imageAddress(0), symbolAddress(0) {}
+ uint64_t address;
+ uint64_t imageAddress;
+ uint64_t symbolAddress;
+ int result;
+ };
+
+ struct dlclose : event_pair {
+ dlclose(ktrace_event_t E) : event_pair(E), handle(E->arg2) {}
+ uint64_t handle;
+ int result;
+ };
+
+ struct app_launch : event_pair {
+ app_launch(ktrace_event_t E) : event_pair(E), address(E->arg2) {}
+ uint64_t address;
+ uint64_t launchMode;
+ std::vector<event_pair *> _children;
+ };
+
+ struct static_init : event_pair {
+ static_init(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+ uint64_t libraryAddress;
+ uint64_t funcAddress;
+ };
+
+ struct map_image : event_pair {
+ map_image(ktrace_event_t E) : event_pair(E), path(stringForID(E->arg2)) {}
+ std::string path;
+ uint64_t result;
+ };
+
+ struct apply_fixups : event_pair {
+ apply_fixups(ktrace_event_t E) : event_pair(E) {}
+ };
+
+ struct attach_signature : event_pair {
+ attach_signature(ktrace_event_t E) : event_pair(E) {}
+ };
+
+ struct build_closure : event_pair {
+ build_closure(ktrace_event_t E) : event_pair(E) {}
+ };
+
+ struct add_image_callback : event_pair {
+ add_image_callback(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+ uint64_t libraryAddress;
+ uint64_t funcAddress;
+ };
+
+ struct remove_image_callback : event_pair {
+ remove_image_callback(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+ uint64_t libraryAddress;
+ uint64_t funcAddress;
+
+ };
+
+ struct objc_image_init : event_pair {
+ objc_image_init(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2) {}
+ uint64_t libraryAddress;
+ };
+
+ struct objc_images_map : event_pair {
+ objc_images_map(ktrace_event_t E) : event_pair(E) {}
+ };
+
+ std::shared_ptr<event_pair> _currentRootEvent;
+ std::vector<std::shared_ptr<event_pair>> _eventStack;
+ std::vector<std::shared_ptr<event_pair>> _rootEvents;
+};
+
+struct OutputManager {
+ std::map<unsigned long, std::unique_ptr<output_renderer>> sOutputRenders;
+
+ void flush() {
+ if (JSON_Tracing_flag) {
+ std::ostringstream ostream;
+ ostream << "{\"displayTimeUnit\":\"ns\"";
+ ostream << ", \"traceEvents\": [";
+ bool firstEvent = true;
+ for (const auto& renderer : sOutputRenders) {
+ for (const auto& root : renderer.second->rootEvents()) {
+ if (firstEvent)
+ firstEvent = false;
+ else
+ ostream << ", ";
+ renderer.second->outputTracingJSON(root, ostream);
+ }
+ }
+ ostream << "]";
+ ostream << "}" << std::endl;
+ std::cout << ostream.str();
+ if (!RAW_flag)
+ fflush(stdout);
+ }
+ }
+};
+
+static OutputManager sOutputManager;
+
+void
+setup_ktrace_callbacks(void)
+{
+ ktrace_events_single(s, TRACEDBG_CODE(DBG_TRACE_STRING, TRACE_STRING_GLOBAL), ^(ktrace_event_t event){
+ char argChars[33] = {0};
+ memset(&argChars[0], 0, 33);
+ if ((event->debugid & DBG_FUNC_START) == DBG_FUNC_START) {
+ uint64_t str_id = event->arg2;
+ if (((event->debugid & DBG_FUNC_END) == DBG_FUNC_END)
+ && str_id != 0) {
+ auto i = gActiveStrings.find(str_id);
+ if (i != gActiveStrings.end()) {
+ gActiveStrings.erase(i);
+ }
+ }
+ *((uint64_t*)&argChars[0]) = event->arg3;
+ *((uint64_t*)&argChars[8]) = event->arg4;
+ gActiveStringIDs.insert(std::make_pair(event->threadid, str_id));
+ gActiveStrings.insert(std::make_pair(str_id, argChars));
+ } else {
+ // Not a start, so lets grab our data
+ *((uint64_t*)&argChars[0]) = event->arg1;
+ *((uint64_t*)&argChars[8]) = event->arg2;
+ *((uint64_t*)&argChars[16]) = event->arg3;
+ *((uint64_t*)&argChars[24]) = event->arg4;
+
+ auto i = gActiveStringIDs.find(event->threadid);
+ if (i != gActiveStringIDs.end()) {
+ auto j = gActiveStrings.find(i->second);
+ if (j != gActiveStrings.end()) {
+ j->second += argChars;
+ }
+ }
+ }
+
+ if ((event->debugid & DBG_FUNC_END) == DBG_FUNC_END) {
+ auto i = gActiveStringIDs.find(event->threadid);
+ if (i != gActiveStringIDs.end()) {
+ gActiveStringIDs.erase(i);
+ }
+ };
+ });
+
+ // Event though our events are paired, we process them individually so we can
+ // render nested events
+ ktrace_events_range(s, KDBG_EVENTID(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 0), KDBG_EVENTID(DBG_DYLD, DBG_DYLD_API_SUBCLASS+1, 0), ^(ktrace_event_t event){
+ assert((event->debugid & KDBG_FUNC_MASK) != 0);
+ auto i = sOutputManager.sOutputRenders.find(event->threadid);
+ if (i == sOutputManager.sOutputRenders.end()) {
+ sOutputManager.sOutputRenders.emplace(std::make_pair(event->threadid, std::make_unique<output_renderer>(s, event)));
+ i = sOutputManager.sOutputRenders.find(event->threadid);
+ }
+ i->second->recordEvent(event);
+ if (i->second->empty()) {
+ sOutputManager.sOutputRenders.erase(i);
+ }
+ });
+}
+
+int
+main(int argc, char *argv[])
+{
+ char ch;
+ int rv = 0;
+ bool exclude_pids = false;
+ uint64_t time_limit_ns = 0;
+
+ get_screenwidth();
+
+ s = ktrace_session_create();
+ assert(s);
+
+ while ((ch = getopt(argc, argv, "jJeR:t:")) != -1) {
+ switch (ch) {
+ case 'j':
+ JSON_flag = true;
+ break;
+ case 'J':
+ JSON_Tracing_flag = true;
+ break;
+ case 'e':
+ exclude_pids = true;
+ break;
+ case 't':
+ time_limit_ns = (uint64_t)(NSEC_PER_SEC * atof(optarg));
+ if (time_limit_ns == 0) {
+ fprintf(stderr, "ERROR: could not set time limit to %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'R':
+ RAW_flag = true;
+ rv = ktrace_set_file(s, optarg);
+ if (rv) {
+ fprintf(stderr, "ERROR: reading trace from '%s' failed (%s)\n", optarg, strerror(errno));
+ exit(1);
+ }
+ break;
+ default:
+ exit_usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (time_limit_ns > 0) {
+ if (RAW_flag) {
+ fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n");
+ } else {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns),
+ dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
+ ^{
+ ktrace_end(s, 0);
+ });
+ }
+ }
+
+ if (!RAW_flag) {
+ if (geteuid() != 0) {
+ fprintf(stderr, "'dyld_usage' must be run as root...\n");
+ exit(1);
+ }
+
+ /*
+ * ktrace can't both *in*clude and *ex*clude pids, so: if we are
+ * already excluding pids, or if we are not explicitly including
+ * or excluding any pids, then exclude the defaults.
+ *
+ * if on the other hand we are explicitly including pids, we'll
+ * filter the defaults out naturally.
+ */
+ if (exclude_pids || argc == 0) {
+ ktrace_exclude_process(s, "dyld_usage");
+ ktrace_exclude_process(s, "Terminal");
+ ktrace_exclude_process(s, "telnetd");
+ ktrace_exclude_process(s, "telnet");
+ ktrace_exclude_process(s, "sshd");
+ ktrace_exclude_process(s, "rlogind");
+ ktrace_exclude_process(s, "tcsh");
+ ktrace_exclude_process(s, "csh");
+ ktrace_exclude_process(s, "sh");
+ ktrace_exclude_process(s, "zsh");
+#if TARGET_OS_EMBEDDED
+ ktrace_exclude_process(s, "dropbear");
+#endif /* TARGET_OS_EMBEDDED */
+ }
+ }
+
+ /*
+ * Process the list of specified pids, and in/exclude them as
+ * appropriate.
+ */
+ while (argc > 0) {
+ pid_t pid;
+ char *name;
+ char *endptr;
+
+ name = argv[0];
+ pid = (pid_t)strtoul(name, &endptr, 10);
+
+ if (*name != '\0' && *endptr == '\0') {
+ if (exclude_pids) {
+ rv = ktrace_exclude_pid(s, pid);
+ } else {
+ if (pid != 0)
+ rv = ktrace_filter_pid(s, pid);
+ }
+ } else {
+ if (exclude_pids) {
+ rv = ktrace_exclude_process(s, name);
+ } else {
+ if (strcmp(name, "kernel_task"))
+ rv = ktrace_filter_process(s, name);
+ }
+ }
+
+ if (rv == EINVAL) {
+ fprintf(stderr, "ERROR: cannot both include and exclude simultaneously\n");
+ exit(1);
+ } else {
+ assert(!rv);
+ }
+
+ argc--;
+ argv++;
+ }
+ /* provides SIGINT, SIGHUP, SIGPIPE, SIGTERM handlers */
+ ktrace_set_signal_handler(s);
+ ktrace_set_completion_handler(s, ^{
+ sOutputManager.flush();
+ exit(0);
+ });
+
+ signal(SIGWINCH, SIG_IGN);
+ sigwinch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGWINCH, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(sigwinch_source, ^{
+ get_screenwidth();
+ });
+ dispatch_activate(sigwinch_source);
+
+ setup_ktrace_callbacks();
+
+ ktrace_set_dropped_events_handler(s, ^{
+ fprintf(stderr, "dyld_usage: buffer overrun, events generated too quickly\n");
+
+ /* clear any state that is now potentially invalid */
+ });
+
+ ktrace_set_execnames_enabled(s, KTRACE_FEATURE_LAZY);
+ ktrace_set_vnode_paths_enabled(s, false);
+ /* no need to symbolicate addresses */
+ ktrace_set_uuid_map_enabled(s, KTRACE_FEATURE_DISABLED);
+
+ rv = ktrace_start(s, dispatch_get_main_queue());
+
+ if (rv) {
+ perror("ktrace_start");
+ exit(1);
+ }
+
+ dispatch_main();
+
+ return 0;
+}
* @APPLE_LICENSE_HEADER_END@
*/
+#define _FORTIFY_SOURCE 0
+
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <libkern/OSAtomic.h>
#include <errno.h>
#include <pthread.h>
+#include <corecrypto/ccdigest.h>
+#include <corecrypto/ccsha1.h>
+#include <corecrypto/ccsha2.h>
+
#if TARGET_IPHONE_SIMULATOR
#include "dyldSyscallInterface.h"
#include "dyld_images.h"
typedef struct mach_header macho_header;
typedef struct nlist macho_nlist;
#endif
+
+ #define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE (32*1024)
+ #define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID 0x1000
+ #define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID 0x2000
+ #define DYLD_PROCESS_INFO_NOTIFY_MAIN_ID 0x3000
+
+ struct dyld_process_info_image_entry {
+ uuid_t uuid;
+ uint64_t loadAddress;
+ uint32_t pathStringOffset;
+ uint32_t pathLength;
+ };
+
+ struct dyld_process_info_notify_header {
+ mach_msg_header_t header;
+ uint32_t version;
+ uint32_t imageCount;
+ uint32_t imagesOffset;
+ uint32_t stringsOffset;
+ uint64_t timestamp;
+ };
#endif
// from _simple.h in libc
}
// std::__terminate() called by C++ unwinding code
-void _ZSt11__terminatePFvvE(void (*func)())
+void _ZSt11__terminatePFvvE(void (*func)(void))
{
_ZN4dyld4haltEPKc("dyld std::__terminate()\n");
}
// std::__unexpected() called by C++ unwinding code
-void _ZSt12__unexpectedPFvvE(void (*func)())
+void _ZSt12__unexpectedPFvvE(void (*func)(void))
{
_ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
}
// referenced by libc.a(pthread.o) but unneeded in dyld
-void _init_cpu_capabilities() { }
-void _cpu_capabilities() {}
void set_malloc_singlethreaded() {}
int PR_5243343_flag = 0;
FILE* __stdoutp = NULL;
// work with c++abi.a
-void (*__cxa_terminate_handler)() = _ZSt9terminatev;
-void (*__cxa_unexpected_handler)() = _ZSt10unexpectedv;
+void (*__cxa_terminate_handler)(void) = _ZSt9terminatev;
+void (*__cxa_unexpected_handler)(void) = _ZSt10unexpectedv;
void abort_message(const char* format, ...)
{
#if TARGET_IPHONE_SIMULATOR
-#include <coreSymbolicationDyldSupport.h>
-
int myopen(const char* path, int oflag, int extra) __asm("_open");
int myopen(const char* path, int oflag, int extra) {
return gSyscallHelpers->open(path, oflag, extra);
return gSyscallHelpers->readdir_r(dirp, entry, result);
}
+// HACK: readdir() is not used in dyld_sim, but it is pulled in by libc.a, then dead stripped.
+struct dirent* readdir(DIR *dirp) {
+ _ZN4dyld4haltEPKc("dyld_sim readdir() not supported\n");
+}
+
int closedir(DIR* dirp) {
if ( gSyscallHelpers->version < 3 )
return EPERM;
return gSyscallHelpers->closedir(dirp);
}
-void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
{
// if host dyld supports this notifier, call into host dyld
if ( gSyscallHelpers->version >= 4 )
return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh);
}
-void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+void coresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
{
// if host dyld supports this notifier, call into host dyld
if ( gSyscallHelpers->version >= 4 )
#if SUPPORT_HOST_10_11
typedef int (*FuncPtr_proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t bufferSize);
-typedef pid_t (*FuncPtr_getpid)();
+typedef pid_t (*FuncPtr_getpid)(void);
typedef bool (*FuncPtr_mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+typedef void (*FuncPtr_mach_msg_destroy)(mach_msg_header_t *);
+typedef kern_return_t (*FuncPtr_mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
+typedef kern_return_t (*FuncPtr_mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
+typedef void (*FuncPtr_notifyMonitoringDyld)(bool unloading, unsigned portSlot, unsigned imageCount, const struct dyld_image_info infos[]);
static FuncPtr_proc_regionfilename proc_proc_regionfilename = NULL;
static FuncPtr_getpid proc_getpid = NULL;
static FuncPtr_mach_port_insert_right proc_mach_port_insert_right = NULL;
static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL;
static FuncPtr_mach_msg proc_mach_msg = NULL;
+static FuncPtr_mach_msg_destroy proc_mach_msg_destroy = NULL;
+static FuncPtr_mach_port_construct proc_mach_port_construct = NULL;
+static FuncPtr_mach_port_destruct proc_mach_port_destruct = NULL;
+static FuncPtr_notifyMonitoringDyld proc_notifyMonitoringDyld = NULL;
else if ( strcmp(name, "_mach_port_allocate") == 0 )
proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide);
else if ( strcmp(name, "_mach_msg") == 0 )
- proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
+ proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
+ else if ( strcmp(name, "__ZN4dyldL20notifyMonitoringDyldEbjjPK15dyld_image_info") == 0 )
+ proc_notifyMonitoringDyld = (FuncPtr_notifyMonitoringDyld)(s->n_value + slide);
}
}
}
+
+// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim
+static bool findHostLibSystemFunctions() {
+ // Only look up symbols once
+ if (proc_mach_msg_destroy != NULL && proc_mach_port_construct != NULL && proc_mach_port_destruct != NULL)
+ return true;
+
+ const struct mach_header* hostLibSystemMH = NULL;
+ struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
+ const struct dyld_image_info* infoArray = imageInfo->infoArray;
+ if (infoArray == NULL)
+ return false;
+ uint32_t imageCount = imageInfo->infoArrayCount;
+ for (uint32_t i = 0; i<imageCount; ++i) {
+ if (strcmp("/usr/lib/system/libsystem_kernel.dylib", infoArray[i].imageFilePath) == 0) {
+ //Found the kernel interface
+ hostLibSystemMH = infoArray[i].imageLoadAddress;
+ break;
+ }
+ }
+ if (hostLibSystemMH == NULL)
+ return false;
+
+ // find symbol table and slide of host dyld
+ uintptr_t slide = 0;
+ const macho_nlist* symbolTable = NULL;
+ const char* symbolTableStrings = NULL;
+ const struct dysymtab_command* dynSymbolTable = NULL;
+ const uint32_t cmd_count = hostLibSystemMH->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)hostLibSystemMH)+sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ const uint8_t* linkEditBase = NULL;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+ slide = (uintptr_t)hostLibSystemMH - seg->vmaddr;
+ if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
+ linkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff + slide);
+ }
+ break;
+ case LC_SYMTAB:
+ {
+ const struct symtab_command* symtab = (struct symtab_command*)cmd;
+ if ( linkEditBase == NULL )
+ return false;
+ symbolTableStrings = (const char*)&linkEditBase[symtab->stroff];
+ symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
+ }
+ break;
+ case LC_DYSYMTAB:
+ dynSymbolTable = (struct dysymtab_command*)cmd;
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ if ( symbolTableStrings == NULL )
+ return false;;
+ if ( dynSymbolTable == NULL )
+ return false;;
+
+ // scan local symbols in host dyld looking for load/unload functions
+ const macho_nlist* const localsStart = &symbolTable[dynSymbolTable->iextdefsym];
+ const macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nextdefsym];
+ for (const macho_nlist* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+ const char* name = &symbolTableStrings[s->n_un.n_strx];
+ if ( strcmp(name, "_mach_msg_destroy") == 0 )
+ proc_mach_msg_destroy = (FuncPtr_mach_msg_destroy)(s->n_value + slide);
+ else if ( strcmp(name, "_mach_port_construct") == 0 )
+ proc_mach_port_construct = (FuncPtr_mach_port_construct)(s->n_value + slide);
+ else if ( strcmp(name, "_mach_port_destruct") == 0 )
+ proc_mach_port_destruct = (FuncPtr_mach_port_destruct)(s->n_value + slide);
+ }
+ }
+ return (proc_mach_msg_destroy != NULL && proc_mach_port_construct != NULL && proc_mach_port_destruct != NULL);
+}
#endif
#endif
}
+void mach_msg_destroy(mach_msg_header_t *msg) {
+ if ( gSyscallHelpers->version >= 12 ) {
+ gSyscallHelpers->mach_msg_destroy(msg);
+ return;
+ }
+#if SUPPORT_HOST_10_11
+ if (findHostLibSystemFunctions()) {
+ (*proc_mach_msg_destroy)(msg);
+ }
+#endif
+}
+
+kern_return_t mach_port_construct(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name) {
+ if ( gSyscallHelpers->version >= 12 ) {
+ return gSyscallHelpers->mach_port_construct(task, options, context, name);
+ }
+#if SUPPORT_HOST_10_11
+ if (findHostLibSystemFunctions()) {
+ return (*proc_mach_port_construct)(task, options, context, name);
+ }
+#endif
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t mach_port_destruct(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard) {
+ if ( gSyscallHelpers->version >= 12 ) {
+ return gSyscallHelpers->mach_port_destruct(task, name, srdelta, guard);
+ }
+#if SUPPORT_HOST_10_11
+ if (findHostLibSystemFunctions()) {
+ return (*proc_mach_port_destruct)(task, name, srdelta, guard);
+ }
+#endif
+ return KERN_NOT_SUPPORTED;
+}
void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags)
{
return 0;
}
+uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str) {
+ if ( gSyscallHelpers->version >= 9 )
+ return gSyscallHelpers->kdebug_trace_string(debugid, str_id, str);
+ return 0;
+}
+
+uint64_t amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags)
+{
+ if ( gSyscallHelpers->version >= 10 )
+ return gSyscallHelpers->amfi_check_dyld_policy_self(inFlags, outFlags);
+ *outFlags = 0x3F; // on old kernel, simulator process get all flags
+ return 0;
+}
+
+static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+
+void _ZN4dyld24notifyMonitoringDyldMainEv() {
+ if ( gSyscallHelpers->version >= 11 ) {
+ gSyscallHelpers->notifyMonitoringDyldMain();
+ return;
+ }
+ struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ if ( (imageInfo->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 = imageInfo->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]);
+ mach_port_deallocate(mach_task_self(), imageInfo->notifyPorts[slot]);
+ mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+ imageInfo->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 _ZN4dyld20notifyMonitoringDyldEbjPPK11mach_headerPPKc(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]) {
+ if ( gSyscallHelpers->version >= 11 ) {
+ gSyscallHelpers->notifyMonitoringDyld(unloading, imageCount, loadAddresses, imagePaths);
+ return;
+ }
+#if SUPPORT_HOST_10_11
+ findHostFunctions();
+ if ( proc_notifyMonitoringDyld ) {
+ struct dyld_image_info infos[imageCount];
+ for (int i=0; i<imageCount; ++i) {
+ struct dyld_image_info info = { loadAddresses[i], imagePaths[i], 0};
+ infos[i] = info;
+ }
+ for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+ (*proc_notifyMonitoringDyld)(unloading, slot, imageCount, infos);
+ }
+ }
+#endif
+}
+
int* __error(void) {
return gSyscallHelpers->errnoAddress();
}
extern int myerrno_fallback __asm("_errno");
int myerrno_fallback = 0;
+
+vm_size_t vm_kernel_page_mask = 0xFFF;
+vm_size_t vm_page_size = 0x1000;
+
#endif // TARGET_IPHONE_SIMULATOR
}
+unsigned char* CC_SHA384(const void* data, unsigned long len, unsigned char* md)
+{
+ const struct ccdigest_info *di = ccsha384_di();
+ ccdigest_di_decl(di, dc);//declares dc array in stack
+ ccdigest_init(di, dc);
+ ccdigest_update(di, dc, len, data);
+ ccdigest_final(di, dc, md);
+ ccdigest_di_clear(di, dc);
+ return NULL;
+}
+
+
+unsigned char* CC_SHA256(const void* data, unsigned long len, unsigned char* md)
+{
+ const struct ccdigest_info *di = ccsha256_di();
+ ccdigest_di_decl(di, dc);//declares dc array in stack
+ ccdigest_init(di, dc);
+ ccdigest_update(di, dc, len, data);
+ ccdigest_final(di, dc, md);
+ ccdigest_di_clear(di, dc);
+ return NULL;
+}
+
+unsigned char* CC_SHA1(const void* data, unsigned long len, unsigned char* md)
+{
+ const struct ccdigest_info *di = ccsha1_di();
+ ccdigest_di_decl(di, dc);//declares dc array in stack
+ ccdigest_init(di, dc);
+ ccdigest_update(di, dc, len, data);
+ ccdigest_final(di, dc, md);
+ ccdigest_di_clear(di, dc);
+ return NULL;
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n)
+{
+ errno_t err = 0;
+ if (s == NULL)
+ return EINVAL;
+ if (n > smax) {
+ err = EOVERFLOW;
+ n = smax;
+ }
+ memset(s, c, n);
+ return err;
+}
+#endif
+++ /dev/null
-/*
- * Copyright (c) 2013 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 __START_GLUE_H__
-#define __START_GLUE_H__
-
-// Implemented in start_glue.s
-extern "C" void start();
-
-
-// <rdar://problem/12792039> need 'start' to be one atom, but entry is in interior
-
-#if __x86_64__ || __i386__
- #define address_of_start (void*)((uintptr_t)&start + 1)
-#elif __arm64__
- #define address_of_start (void*)((uintptr_t)&start + 4)
-#elif __arm__
- #define address_of_start (void*)((uintptr_t)&start + 2)
-#endif
-
-
-
-#endif // __START_GLUE_H__
.globl _tlv_get_addr
.private_extern _tlv_get_addr
_tlv_get_addr:
+#if __LP64__
ldr x16, [x0, #8] // get key from descriptor
+#else
+ ldr w16, [x0, #4] // get key from descriptor
+#endif
mrs x17, TPIDRRO_EL0
and x17, x17, #-8 // clear low 3 bits???
+#if __LP64__
ldr x17, [x17, x16, lsl #3] // get thread allocation address for this key
+#else
+ ldr w17, [x17, x16, lsl #2] // get thread allocation address for this key
+#endif
cbz x17, LlazyAllocate // if NULL, lazily allocate
+#if __LP64__
ldr x16, [x0, #16] // get offset from descriptor
+#else
+ ldr w16, [x0, #8] // get offset from descriptor
+#endif
add x0, x17, x16 // return allocation+offset
ret lr
mov x0, x16 // use key from descriptor as parameter
bl _tlv_allocate_and_initialize_for_key
ldp x16, x17, [sp], #16 // pop descriptor
+#if __LP64__
ldr x16, [x16, #16] // get offset from descriptor
+#else
+ ldr w16, [x16, #8] // get offset from descriptor
+#endif
add x0, x0, x16 // return allocation+offset
ldp q6, q7, [sp], #32
runLines = []
minOS = ""
timeout = ""
+ noCrashLogs = []
for file in os.listdir(testCaseSourceDir):
if file.endswith((".c", ".cpp", ".cxx")):
with open(testCaseSourceDir + "/" + file) as f:
timeoutIndex = string.find(line, "RUN_TIMEOUT:")
if timeoutIndex != -1:
timeout = line[timeoutIndex+12:].lstrip()
-
+ noCrashLogsIndex = string.find(line, "NO_CRASH_LOG:")
+ if noCrashLogsIndex != -1:
+ noCrashLogs.append(line[noCrashLogsIndex+13:].lstrip())
return {
"BUILD": buildLines,
"BUILD_ONLY": onlyLines,
"BUILD_MIN_OS": minOS,
"RUN": runLines,
- "RUN_TIMEOUT": timeout
+ "RUN_TIMEOUT": timeout,
+ "NO_CRASH_LOG": noCrashLogs
}
# Use BUILD directives to construct the test case
# Use RUN directives to generate a shell script to run test(s)
#
-def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun):
+def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, dyldIncludesDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun):
scratchDir = tempfile.mkdtemp()
if testCaseDirectives["BUILD_MIN_OS"]:
minOS = testCaseDirectives["BUILD_MIN_OS"]
else:
minOS = defaultMinOS
- compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders"
+ compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders" + " -I" + dyldIncludesDir
if minOsOptionsName == "mmacosx-version-min":
taskForPidCommand = "touch "
envEnableCommand = "touch "
runFile.write("#!/bin/sh\n")
runFile.write("cd " + testCaseDestDirRun + "\n")
os.chmod(runFilePath, 0755)
+ runFile.write("echo \"run in dyld2 mode\" \n");
for runline in testCaseDirectives["RUN"]:
runFile.write(string.Template(runline).safe_substitute(runSubs) + "\n")
+ runFile.write("echo \"run in dyld3 mode\" \n");
+ for runline in testCaseDirectives["RUN"]:
+ subLine = string.Template(runline).safe_substitute(runSubs)
+ if subLine.startswith("sudo "):
+ subLine = "sudo DYLD_USE_CLOSURES=1 " + subLine[5:]
+ else:
+ subLine = "DYLD_USE_CLOSURES=1 " + subLine
+ runFile.write(subLine + "\n")
runFile.write("\n")
runFile.close()
return 0
if not dyldSrcDir:
dyldSrcDir = os.getcwd()
testsSrcTopDir = dyldSrcDir + "/testing/test-cases/"
+ dyldIncludesDir = dyldSrcDir + "/include/"
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"
+ sdkDir = subprocess.check_output(["xcrun", "-sdk", "macosx.internal", "--show-sdk-path"]).rstrip()
toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
defaultMinOS = ""
- minVersNum = "10.12"
+ minVersNum = "10.14"
minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
if minOSOption:
minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
else:
archOptions = "-arch x86_64"
allTests = []
- for f in os.listdir(testsSrcTopDir):
+ suppressCrashLogs = []
+ for f in sorted(os.listdir(testsSrcTopDir)):
if f.endswith(".dtest"):
testName = f[0:-6]
outDirBuild = testsBuildDstTopDir + testName
testCaseDir = testsSrcTopDir + f
testCaseDirectives = parseDirectives(testCaseDir)
if useTestCase(testCaseDirectives, platformName):
- result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun)
+ result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, dyldIncludesDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun)
if result:
sys.exit(result)
mytest = {}
mytest["WorkingDirectory"] = testsRunDstTopDir + testName
mytest["Command"] = []
mytest["Command"].append("./run.sh")
+ usesDtrace = False
for runline in testCaseDirectives["RUN"]:
if "$SUDO" in runline:
mytest["AsRoot"] = True
+ if "dtrace" in runline:
+ usesDtrace = True
if testCaseDirectives["RUN_TIMEOUT"]:
mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"]
+ if usesDtrace and minOSOption != "mmacosx-version-min":
+ mytest["BootArgsSet"] = "dtrace_dof_mode=1"
allTests.append(mytest)
+ if testCaseDirectives["NO_CRASH_LOG"]:
+ for skipCrash in testCaseDirectives["NO_CRASH_LOG"]:
+ suppressCrashLogs.append(skipCrash)
batsInfo = { "BATSConfigVersion": "0.1.0",
"Project": "dyld_tests",
"Tests": allTests }
+ if suppressCrashLogs:
+ batsInfo["IgnoreCrashes"] = []
+ for skipCrash in suppressCrashLogs:
+ batsInfo["IgnoreCrashes"].append(skipCrash)
batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/"
shutil.rmtree(batsFileDir, ignore_errors=True)
os.makedirs(batsFileDir)
shFile.close()
os.chmod(runHelper, 0755)
-
--- /dev/null
+void foo()
+{
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/hideyhole
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo1.dylib -install_name /bad/path/libfoo1.dylib
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo2.dylib -install_name /bad/path2/libfoo2.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/main1.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@executable_path/hideyhole
+// BUILD: $CC main.c -o $BUILD_DIR/main2.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/hideyhole
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main1.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main2.exe
+
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main1.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main2.exe
+
+// RUN: ./main1.exe
+// RUN: ./main2.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+/// Test that main executable's LC_DYLD_ENVIRONMENT can set DYLD_LIBRARY_PATH with @executable_path or @loader_path relative paths
+
+extern char* __progname;
+
+int main()
+{
+ printf("[BEGIN] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+
+ void*h = dlopen("/other/path/libfoo2.dylib", 0);
+
+ if ( h != NULL )
+ printf("[PASS] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+ else
+ printf("[FAIL] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+
+ return 0;
+}
+
--- /dev/null
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CXX main.cpp -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations
+// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN: ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <vector>
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+
+ const char* path = argv[1];
+
+ std::vector<NSObjectFileImage> ofis;
+ for (unsigned i = 0; i != 32; ++i) {
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+ printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
+ return 0;
+ }
+ ofis.push_back(ofi);
+ }
+
+ for (unsigned i = 0; i != 32; ++i) {
+ NSObjectFileImage ofi = ofis[i];
+ 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;
+ }
+ }
+
+ for (unsigned i = 0; i != 32; ++i) {
+ NSObjectFileImage ofi = ofis[i];
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ printf("[FAIL] NSDestroyObjectFileImage failed\n");
+ return 0;
+ }
+ }
+
+ printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
+ return 0;
+}
+
// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle
+// BUILD: lipo -thin x86_64 $BUILD_DIR/foo.bundle -output $BUILD_DIR/foo-thin.bundle
// RUN: ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle
+// RUN: ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo-thin.bundle
+
#include <stdio.h>
--- /dev/null
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int foo1()
+{
+ return 1;
+}
+
+int foo2()
+{
+ return 2;
+}
+
+int foo3()
+{
+ return 3;
+}
+
+
--- /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/dyld_images_for_addresses.exe
+
+// RUN: ./dyld_images_for_addresses.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid/uuid.h>
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+extern int foo1();
+extern int foo2();
+extern int foo3();
+
+
+int myfunc()
+{
+ return 3;
+}
+
+int myfunc2()
+{
+ return 3;
+}
+
+static int mystaticfoo()
+{
+ return 3;
+}
+
+int mydata = 5;
+int myarray[10];
+
+
+int main()
+{
+ printf("[BEGIN] _dyld_images_for_addresses\n");
+ int mylocal;
+
+ const void* addresses[10];
+ addresses[0] = &myfunc;
+ addresses[1] = &myfunc2;
+ addresses[2] = &mystaticfoo;
+ addresses[3] = &__dso_handle;
+ addresses[4] = &mydata;
+ addresses[5] = &myarray;
+ addresses[6] = &mylocal; // not owned by dyld, so coresponding dyld_image_uuid_offset should be all zeros
+ addresses[7] = &foo1;
+ addresses[8] = &foo2;
+ addresses[9] = &foo3;
+
+ struct dyld_image_uuid_offset infos[10];
+ _dyld_images_for_addresses(10, addresses, infos);
+
+ for (int i=0; i < 10; ++i) {
+ uuid_string_t str;
+ uuid_unparse_upper(infos[i].uuid, str);
+ printf("0x%09llX 0x%08llX %s\n", (long long)infos[i].image, infos[i].offsetInImage, str);
+ }
+
+ if ( infos[0].image != infos[1].image )
+ printf("[FAIL] _dyld_images_for_addresses 1\n");
+ else if ( infos[0].image != infos[2].image )
+ printf("[FAIL] _dyld_images_for_addresses 2\n");
+ else if ( infos[0].image != infos[3].image )
+ printf("[FAIL] _dyld_images_for_addresses 3\n");
+ else if ( infos[0].image != infos[4].image )
+ printf("[FAIL] _dyld_images_for_addresses 4\n");
+ else if ( infos[0].image != infos[5].image )
+ printf("[FAIL] _dyld_images_for_addresses 5\n");
+ else if ( infos[6].image != NULL )
+ printf("[FAIL] _dyld_images_for_addresses 6\n");
+ else if ( infos[7].image != infos[8].image )
+ printf("[FAIL] _dyld_images_for_addresses 7\n");
+ else if ( infos[7].image != infos[9].image )
+ printf("[FAIL] _dyld_images_for_addresses 8\n");
+ else if ( infos[0].image == infos[7].image )
+ printf("[FAIL] _dyld_images_for_addresses 9\n");
+ else if ( uuid_compare(infos[0].uuid, infos[1].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 10\n");
+ else if ( uuid_compare(infos[0].uuid, infos[2].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 11\n");
+ else if ( uuid_compare(infos[0].uuid, infos[3].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 12\n");
+ else if ( uuid_compare(infos[0].uuid, infos[4].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 13\n");
+ else if ( uuid_compare(infos[0].uuid, infos[5].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 14\n");
+ else if ( uuid_is_null(infos[6].uuid) == 0 )
+ printf("[FAIL] _dyld_images_for_addresses 15\n");
+ else if ( uuid_compare(infos[7].uuid, infos[8].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 16\n");
+ else if ( uuid_compare(infos[7].uuid, infos[9].uuid) != 0 )
+ printf("[FAIL] _dyld_images_for_addresses 17\n");
+ else if ( uuid_compare(infos[0].uuid, infos[7].uuid) == 0 )
+ printf("[FAIL] _dyld_images_for_addresses 18\n");
+ else
+ printf("[PASS] _dyld_images_for_addresses\n");
+ return 0;
+}
+
#include <mach-o/dyld.h>
#include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
+
typedef const char* (*BarProc)(void);
extern uint32_t _cpu_capabilities;
return 0;
}
- if ( !_dyld_is_memory_immutable(&strcpy, 4) ) {
- printf("[FAIL] _dyld_is_memory_immutable() returned false for function in dyld shared cache\n");
+ if ( !_dyld_is_memory_immutable(stripPointer((void*)&strcpy), 4) ) {
+ printf("[FAIL] _dyld_is_memory_immutable() returned false for strcpy function in dyld shared cache\n");
return 0;
}
--- /dev/null
+void foo() {}
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CXX main.cxx -o $BUILD_DIR/dyld_register_test.exe $BUILD_DIR/libfoo.dylib -DRUN_DIR="$RUN_DIR"
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/foo.bundle
+
+// 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 "C" void foo();
+
+extern mach_header __dso_handle;
+
+static std::unordered_set<const mach_header*> sCurrentImages;
+
+static void notify(const mach_header* mh, const char* path, bool unloadable)
+{
+ fprintf(stderr, "mh=%p, path=%s, unloadable=%d\n", mh, path, unloadable);
+ if ( sCurrentImages.count(mh) != 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh);
+ exit(0);
+ }
+ sCurrentImages.insert(mh);
+
+ const char* leaf = strrchr(path, '/');
+ if ( unloadable ) {
+ if ( (strcmp(leaf, "/libfoo2.dylib") != 0) && (strcmp(leaf, "/foo.bundle") != 0) ) {
+ printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked unloadable %p %s\n", mh, path);
+ exit(0);
+ }
+ }
+ else {
+ if ( (strcmp(leaf, "/libfoo2.dylib") == 0) || (strcmp(leaf, "/foo.bundle") == 0) ) {
+ printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked as not unloadable %p %s\n", mh, path);
+ exit(0);
+ }
+ }
+}
+
+
+int main()
+{
+ printf("[BEGIN] _dyld_register_for_image_loads\n");
+
+ _dyld_register_for_image_loads(¬ify);
+
+ // verify we were notified about already loaded images
+ if ( sCurrentImages.count(&__dso_handle) == 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads() did not notify us about main executable\n");
+ exit(0);
+ }
+ const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
+ if ( sCurrentImages.count(libSysMH) == 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libsystem_c.dylib\n");
+ exit(0);
+ }
+ const mach_header* libFoo = dyld_image_header_containing_address((void*)&foo);
+ if ( sCurrentImages.count(libFoo) == 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo.dylib\n");
+ exit(0);
+ }
+
+ // verify we were notified about load of libfoo2.dylib
+ void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
+ if ( handle2 == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo.dylib", dlerror());
+ exit(0);
+ }
+ const void* libfoo2Foo = dlsym(handle2, "foo");
+ const mach_header* libfoo2MH = dyld_image_header_containing_address(libfoo2Foo);
+ if ( sCurrentImages.count(libfoo2MH) == 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo2.dylib\n");
+ exit(0);
+ }
+
+ // verify we were notified about load of foo.bundle
+ void* handleB = dlopen(RUN_DIR "/foo.bundle", RTLD_FIRST);
+ if ( handleB == NULL ) {
+ printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/foo.bundle", dlerror());
+ exit(0);
+ }
+ const void* libfooBFoo = dlsym(handle2, "foo");
+ const mach_header* libfooB = dyld_image_header_containing_address(libfooBFoo);
+ if ( sCurrentImages.count(libfooB) == 0 ) {
+ printf("[FAIL] _dyld_register_for_image_loads() did not notify us about foo.bundle\n");
+ exit(0);
+ }
+
+
+
+ printf("[PASS] _dyld_register_for_image_loads\n");
+ return 0;
+}
+
--- /dev/null
+int foo = 42;
--- /dev/null
+
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/cwd-load.exe
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/cwd-load.exe
+
+// RUN: ./cwd-load.exe
+
+// libfoo.dylib is loaded from the current directory (not an absolute path)
+
+
+#include <stdio.h>
+
+extern int foo;
+
+
+int main()
+{
+ printf("[BEGIN] cwd-relative-load\n");
+ if ( foo == 42 )
+ printf("[PASS] cwd-relative-load\n");
+ else
+ printf("[FAIL] cwd-relative-load, wrong value\n");
+
+ return 0;
+}
+
+
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
+
int bar()
{
return 4;
}
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
// checks global symbol
static void verifybar()
{
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &bar) {
+ if ( info.dli_saddr != stripPointer(&bar) ) {
printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &foo) {
+ if ( info.dli_saddr != stripPointer(&foo) ) {
printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &hide) {
+ if ( info.dli_saddr != stripPointer(&hide) ) {
printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &malloc) {
+ if ( info.dli_saddr != stripPointer(&malloc) ) {
printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
exit(0);
}
}
}
+// checks passing NULL for info parameter gracefully fails
+static void verifyNULL()
+{
+ Dl_info info;
+ if ( dladdr(&malloc, NULL) != 0 ) {
+ printf("[FAIL] dladdr(&malloc, NULL) did not fail\n");
+ exit(0);
+ }
+ if ( dladdr(NULL, NULL) != 0 ) {
+ printf("[FAIL] dladdr(NULL, NULL) did not fail\n");
+ exit(0);
+ }
+}
int main()
{
verifyhide();
verifyfoo();
verifymalloc();
-
+ verifyNULL();
printf("[PASS] dladdr-basic\n");
return 0;
#include <string.h>
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
extern void* __dso_handle;
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
+
int dylib_bar()
{
return 2;
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &dylib_bar) {
+ if ( info.dli_saddr != stripPointer(&dylib_bar) ) {
printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &dylib_foo) {
+ if ( info.dli_saddr != stripPointer(&dylib_foo) ) {
printf("[FAIL] dladdr()->dli_saddr is not &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");
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &dylib_hide) {
+ if ( info.dli_saddr != stripPointer(&dylib_hide) ) {
printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n");
exit(0);
}
// 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
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dladdr-dylib.exe
-// RUN: ./dladdr-basic.exe
+// RUN: ./dladdr-dylib.exe
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
extern void* __dso_handle;
extern void verifyDylib();
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
+
int bar()
{
return 2;
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &bar) {
+ if ( info.dli_saddr != stripPointer(&bar) ) {
printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &foo) {
+ if ( info.dli_saddr != stripPointer(&foo) ) {
printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &hide) {
+ if ( info.dli_saddr != stripPointer(&hide) ) {
printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
exit(0);
}
printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
exit(0);
}
- if ( info.dli_saddr != &malloc) {
+ if ( info.dli_saddr != stripPointer(&malloc) ) {
printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
exit(0);
}
int main()
{
- printf("[BEGIN] dladdr-basic\n");
+ printf("[BEGIN] dladdr-dylib\n");
verifybar();
verifyhide();
verifyfoo();
verifyDylib();
- printf("[PASS] dladdr-basic\n");
+ printf("[PASS] dladdr-dylib\n");
return 0;
}
--- /dev/null
+
+
+int __attribute__((weak)) coalA = 1;
+int __attribute__((weak)) coalB = 1;
+
+int foo1_coalA()
+{
+ return coalA;
+}
+
+int foo1_coalB()
+{
+ return coalB;
+}
+
--- /dev/null
+
+
+int __attribute__((weak)) coalA = 2;
+int __attribute__((weak)) coalB = 2;
+int __attribute__((weak)) coalC = 2;
+
+int foo2_coalA()
+{
+ return coalA;
+}
+
+int foo2_coalB()
+{
+ return coalB;
+}
+
+int foo2_coalC()
+{
+ return coalC;
+}
--- /dev/null
+
+
+int __attribute__((weak)) coalA = 3;
+int __attribute__((weak)) coalB = 3;
+int __attribute__((weak)) coalC = 3;
+
+int foo3_coalA()
+{
+ return coalA;
+}
+
+int foo3_coalB()
+{
+ return coalB;
+}
+
+int foo3_coalC()
+{
+ return coalC;
+}
--- /dev/null
+
+// BUILD: $CC foo1.c -dynamiclib -install_name $RUN_DIR/libfoo1.dylib -o $BUILD_DIR/libfoo1.dylib
+// BUILD: $CC foo2.c -dynamiclib -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD: $CC foo3.c -dynamiclib -install_name $RUN_DIR/libfoo3.dylib -o $BUILD_DIR/libfoo3.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-RTLD_LOCAL-coalesce.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-RTLD_LOCAL-coalesce.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+///
+/// This tests the interaction of RTLD_LOCAL and weak-def coalescing.
+///
+/// Normally, (for correct C++ ODR), dyld coalesces all weak-def symbols
+/// across all images, so that only one copy of each weak symbol name
+/// is in use. But, dlopen with RTLD_LOCAL means to "hide" the symbols in
+/// that one (top) image being loaded.
+///
+///
+
+// main *coalA
+// libfoo1.dylib coalA *coalB
+// libfoo2.dylib coalA coalB coalC // loaded with RTLD_LOCAL
+// libfoo3.dylib coalA coalB *coalC
+//
+
+typedef int (*IntProc)(void);
+
+int __attribute__((weak)) coalA = 0;
+
+int main()
+{
+ printf("[BEGIN] dlopen-RTLD_LOCAL-coalesce\n");
+
+ ///
+ /// Load three foo dylibs in order
+ ///
+ void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_GLOBAL);
+ void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_LOCAL);
+ void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_GLOBAL);
+ if ( handle1 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo1.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ if ( handle2 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo2.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ if ( handle3 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo3.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+
+
+ ///
+ /// Get accessor functions
+ ///
+ IntProc foo1_coalA = (IntProc)dlsym(handle1, "foo1_coalA");
+ IntProc foo1_coalB = (IntProc)dlsym(handle1, "foo1_coalB");
+ IntProc foo2_coalA = (IntProc)dlsym(handle2, "foo2_coalA");
+ IntProc foo2_coalB = (IntProc)dlsym(handle2, "foo2_coalB");
+ IntProc foo2_coalC = (IntProc)dlsym(handle2, "foo2_coalC");
+ IntProc foo3_coalA = (IntProc)dlsym(handle3, "foo3_coalA");
+ IntProc foo3_coalB = (IntProc)dlsym(handle3, "foo3_coalB");
+ IntProc foo3_coalC = (IntProc)dlsym(handle3, "foo3_coalC");
+ if ( !foo1_coalA || !foo1_coalB ||
+ !foo2_coalA || !foo2_coalB || !foo2_coalC ||
+ !foo3_coalA || !foo3_coalB || !foo3_coalC ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlsym() failed\n");
+ return 0;
+ }
+
+ ///
+ /// Get values for each coal[ABC] seen in each image
+ ///
+ int foo1A = (*foo1_coalA)();
+ int foo1B = (*foo1_coalB)();
+ int foo2A = (*foo2_coalA)();
+ int foo2B = (*foo2_coalB)();
+ int foo2C = (*foo2_coalC)();
+ int foo3A = (*foo3_coalA)();
+ int foo3B = (*foo3_coalB)();
+ int foo3C = (*foo3_coalC)();
+ printf("coalA in main: %d\n", coalA);
+ printf("coalA in libfoo1: %d\n", foo1A);
+ printf("coalA in libfoo2: %d\n", foo2A);
+ printf("coalA in libfoo3: %d\n", foo3A);
+
+ printf("coalB in libfoo1: %d\n", foo1B);
+ printf("coalB in libfoo2: %d\n", foo2B);
+ printf("coalB in libfoo3: %d\n", foo3B);
+
+ printf("coalC in libfoo2: %d\n", foo2C);
+ printf("coalC in libfoo3: %d\n", foo3C);
+
+
+
+ ///
+ /// Verify coalescing was done properly (foo2 was skipped because of RTLD_LOCAL)
+ ///
+ if ( (foo1A != 0) || (foo2A != 0) || (foo3A != 0) || (coalA != 0) ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalA was not coalesced properly\n");
+ return 0;
+ }
+ if ( (foo1B != 1) || (foo2B != 1) || (foo3B != 1) ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalB was not coalesced properly\n");
+ return 0;
+ }
+ if ( (foo2C != 2) || (foo3C != 3) ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalC was not coalesced properly\n");
+ return 0;
+ }
+
+
+
+ printf("[PASS] dlopen-RTLD_LOCAL-coalesce\n");
+ return 0;
+}
--- /dev/null
+int bar()
+{
+ return 11;
+}
+
--- /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 bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-RTLD_LOCAL-hides.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-RTLD_LOCAL-hides.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-RTLD_LOCAL-hides\n");
+
+ ///
+ /// This tests that RTLD_LOCAL prevents RTLD_DEFAULT from finding symbols, but can be found via handle
+ ///
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LOCAL);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ void* sym = dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ void* sym2 = dlsym(RTLD_DEFAULT, "foo");
+ if ( sym2 != NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed\n");
+ return 0;
+ }
+
+
+ ///
+ /// This tests that RTLD_GLOBAL after RTLD_LOCAL allows RTLD_DEFAULT to find symbols
+ ///
+ void* handle2 = dlopen(RUN_DIR "/libfoo.dylib", RTLD_GLOBAL);
+ if ( handle2 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ void* sym3 = dlsym(RTLD_DEFAULT, "foo");
+ if ( sym3 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL\n");
+ return 0;
+ }
+
+
+ ///
+ /// This tests that RTLD_LOCAL after RTLD_GLOBAL does not block RTLD_DEFAULT from finding symbols
+ ///
+ void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
+ if ( handle3 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ void* handle4 = dlopen(RUN_DIR "/libbar.dylib", RTLD_LOCAL);
+ if ( handle4 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ void* sym4 = dlsym(RTLD_DEFAULT, "bar");
+ if ( sym4 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+
+ printf("[PASS] dlopen-RTLD_LOCAL-hides\n");
+ return 0;
+}
--- /dev/null
+
+
+int bar = 11;
+
--- /dev/null
+
+int foo = 10;
+
--- /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 -o $BUILD_DIR/dlopen-RTLD_NODELETE.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-RTLD_NODELETE.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-RTLD_NODELETE\n");
+
+ ///
+ /// This tests that RTLD_NODELETE on first dlopen() blocks dlclose() from unloading image
+ ///
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NODELETE);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ int* fooSym = (int*)dlsym(handle, "foo");
+ if ( fooSym == NULL ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ int fooValue = *fooSym;
+ dlclose(handle);
+ Dl_info info;
+ if ( dladdr(fooSym, &info) != 0 ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded\n");
+ return 0;
+ }
+ // dereference foo pointer. If RTLD_NODELETE worked, this will not crash
+ if ( *fooSym != fooValue ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: value at fooSym changed\n");
+ return 0;
+ }
+
+ ///
+ /// This tests that RTLD_NODELETE on later dlopen() blocks dlclose() from unloading image
+ ///
+ void* handle2 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
+ if ( handle2 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ int* barSym = (int*)dlsym(handle2, "bar");
+ if ( barSym == NULL ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"bar\") failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ int barValue = *barSym;
+ void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_NODELETE);
+ if ( handle3 == NULL ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
+ return 0;
+ }
+ dlclose(handle2);
+ dlclose(handle3);
+ if ( dladdr(barSym, &info) != 0 ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(barSym, xx) succeeded as if libbar.dylib was not unloaded\n");
+ return 0;
+ }
+ // dereference foo pointer. If RTLD_NODELETE worked, this will not crash
+ if ( *barSym != barValue ) {
+ printf("[FAIL] dlopen-RTLD_NODELETE: value at barSym changed\n");
+ return 0;
+ }
+
+
+ printf("[PASS] dlopen-RTLD_NODELETE\n");
+ return 0;
+}
--- /dev/null
+void bar() {}
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: mkdir -p $BUILD_DIR/test1
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib
+
+// BUILD: mkdir -p $BUILD_DIR/test2
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.dylib
+
+// BUILD: mkdir -p $BUILD_DIR/test3
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3 $BUILD_DIR/test3/libtest3.dylib
+
+// BUILD: mkdir -p $BUILD_DIR/test4
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.dylib
+
+// BUILD: mkdir -p $BUILD_DIR/test5
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib
+// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.dylib
+
+
+
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
+
+// RUN: ./dlopen-restricted.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+int main(int argc, const char* argv[])
+{
+ printf("[BEGIN] dlopen-restricted\n");
+
+ // test1: LC_RPATH not in main executable uses @loader_path
+ void* handle = dlopen(RUN_DIR "/test1.bundle", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-restricted test1.bundle\n");
+ return 0;
+ }
+
+ // test2: @loader_path not in main executable
+ handle = dlopen(RUN_DIR "/test2.bundle", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-restricted test2.bundle\n");
+ return 0;
+ }
+
+ // test3: LC_RPATH not in main executable uses absolute path
+ handle = dlopen(RUN_DIR "/test3.bundle", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-restricted test3.bundle\n");
+ return 0;
+ }
+
+ // test4: [SHOULD FAIL] LC_RPATH not in main executable uses @executable_path
+ handle = dlopen(RUN_DIR "/test4.bundle", RTLD_LAZY);
+ if ( handle != NULL ) {
+ printf("[FAIL] dlopen-restricted test4.bundle dlopen() should not work\n");
+ return 0;
+ }
+
+ // test5: [SHOULD FAIL] @executable_path in LC_LOAD_DYLIB
+ handle = dlopen(RUN_DIR "/test5.bundle", RTLD_LAZY);
+ if ( handle != NULL ) {
+ printf("[FAIL] dlopen-restricted test5.bundle dlopen() should not work\n");
+ return 0;
+ }
+
+
+
+ printf("[PASS] dlopen-restricted\n");
+ return 0;
+}
+
int result = dlclose(handle);
if ( result != 0 ) {
- printf("dlclose() returned %c\n", result);
+ printf("dlclose() returned %d, dlerror()=%s\n", result, dlerror());
printf("[FAIL] dlopen-basic %s\n", path);
return;
}
// 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
+// RUN: ./dlopen-flat.exe
#include <stdio.h>
#include <dlfcn.h>
// Foo exports foo()
void* fooHandle = 0;
{
- const char* path = RUN_DIR "/libfoo.dylib";
- fooHandle = dlopen(path, RTLD_LAZY);
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if (!fooHandle) {
printf("dlopen failed with error: %s\n", dlerror());
return 1;
// Open foo again which should do something.
{
- const char* path = RUN_DIR "/libfoo.dylib";
- fooHandle = dlopen(path, RTLD_LAZY);
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if (!fooHandle) {
printf("dlopen failed with error: %s\n", dlerror());
return 1;
// Bar is going to resolve foo()
void* barHandle = 0;
{
- const char* path = RUN_DIR "/libbar.dylib";
- barHandle = dlopen(path, RTLD_LAZY);
+ barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
if (!barHandle) {
printf("dlopen failed with error: %s\n", dlerror());
return 1;
// Open foo again which shouldn't do anything.
{
- const char* path = RUN_DIR "/libfoo.dylib";
- fooHandle = dlopen(path, RTLD_LAZY);
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if (!fooHandle) {
printf("dlopen failed with error: %s\n", dlerror());
return 1;
--- /dev/null
+#include <stdbool.h>
+
+bool isHaswell()
+{
+#if __x86_64h__
+ return true;
+#else
+ return false;
+#endif
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC a.c -dynamiclib -arch x86_64h -o $BUILD_DIR/libHaswellCheck.dylib -install_name $RUN_DIR/libHaswellCheck.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-haswell.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./dlopen-haswell.exe
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach/host_info.h>
+#include <mach/mach.h>
+#include <mach/mach_host.h>
+
+typedef bool (*BoolFunc)(void);
+
+
+bool isHaswell_dynamic()
+{
+ struct host_basic_info info;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ mach_port_t hostPort = mach_host_self();
+ kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
+ mach_port_deallocate(mach_task_self(), hostPort);
+ if ( result == KERN_SUCCESS ) {
+ return (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
+ }
+ return false;
+}
+
+
+int main(int arg, const char* argv[])
+{
+ printf("[BEGIN] dlopen-haswell\n");
+
+ void* handle = dlopen(RUN_DIR "/libHaswellCheck.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-haswell dlopen\n");
+ return 0;
+ }
+
+ BoolFunc libFunc = (BoolFunc)dlsym(handle, "isHaswell");
+ if ( libFunc == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-haswell dlsym\n");
+ return 0;
+ }
+
+ // check if haswell slice of libHaswellCheck.dylib was loaded on haswell machines
+ bool dylibIsHaswellSlice = (*libFunc)();
+ bool runtimeIsHaswell = isHaswell_dynamic();
+
+ if ( dylibIsHaswellSlice != runtimeIsHaswell )
+ printf("[FAIL] dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d\n", dylibIsHaswellSlice, runtimeIsHaswell);
+ else
+ printf("[PASS] dlopen-haswell\n");
+
+ return 0;
+}
+
+
+
--- /dev/null
+void zzz() {}
--- /dev/null
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+
+
+static void* work1(void* arg)
+{
+ void* h = dlopen("System/Library/Frameworks/Foundation.framework/Foundation", 0);
+
+ return NULL;
+}
+
+
+__attribute__((constructor))
+void myinit()
+{
+ pthread_t workerThread;
+
+ if ( pthread_create(&workerThread, NULL, work1, NULL) != 0 ) {
+ printf("[FAIL] dlopen-in-init, pthread_create\n");
+ return;
+ }
+
+ void* dummy;
+ pthread_join(workerThread, &dummy);
+}
+
--- /dev/null
+
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-in-init.exe $BUILD_DIR/libfoo.dylib
+
+// RUN: ./dlopen-in-init.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+
+__attribute__((constructor))
+void myinit()
+{
+
+}
+
+
+int main()
+{
+ printf("[BEGIN] dlopen-in-init\n");
+
+ // The test is for libdyld.dylib to not crash when libfoo.dylib dlopen() stuff in its initializer
+
+ printf("[PASS] dlopen-in-init\n");
+ return 0;
+}
+
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-realpath.exe
// BUILD: cd $BUILD_DIR && ln -s ./IOKit.framework/IOKit IOKit && ln -s /System/Library/Frameworks/IOKit.framework IOKit.framework
-// RUN: ./dlopen-realpath.exe
+// RUN: DYLD_FALLBACK_LIBRARY_PATH=/baz ./dlopen-realpath.exe
#include <stdio.h>
#include <dlfcn.h>
int result = dlclose(handle);
if ( result != 0 ) {
- printf("dlclose() returned %c\n", result);
+ printf("dlclose(%p): %s\n", handle, dlerror());
printf("[FAIL] dlopen-realpath %s\n", path);
return;
}
{
tryImage("./IOKit.framework/IOKit");
tryImage("./././IOKit/../IOKit.framework/IOKit");
- tryImage("./IOKit");
+ tryImage("./IOKit");
+
+ // Also try libSystem which has an alias in the OS to /usr/lib/libSystem.B.dylib
+ tryImage("/usr/lib/libSystem.dylib");
+
+ // Also try using non-canonical path
+ // This requires DYLD_FALLBACK_LIBRARY_PATH to be disabled, otherwise it is found that way
+ tryImage("//usr/lib/libSystem.dylib");
+ tryImage("/usr/./lib/libSystem.dylib");
return 0;
}
--- /dev/null
+int bar()
+{
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/dir
+// BUILD: $CC bar.c -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib
+// BUILD: $CC test.c -dynamiclib -install_name $RUN_DIR/libtest.dylib -o $BUILD_DIR/libtest.dylib -rpath @loader_path/dir
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-rpath-from-dylib.exe $BUILD_DIR/libtest.dylib
+
+// RUN: ./dlopen-rpath-from-dylib.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+
+/// test that a call to dlopen() from a dylib uses the LC_RPATH from that dylib
+
+extern bool test_dlopen();
+
+int main()
+{
+ printf("[BEGIN] dlopen-rpath-from-dylib\n");
+
+ if ( test_dlopen() )
+ printf("[PASS] dlopen-rpath-from-dylib\n");
+ else
+ printf("[FAIL] dlopen-rpath-from-dylib\n");
+
+ return 0;
+}
+
--- /dev/null
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+bool test_dlopen()
+{
+ void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-rpath-from-dylib: dlopen(@rpath/libbar.dylib) failed: %s\n", dlerror());
+ return false;
+ }
+
+ dlclose(handle);
+
+ return true;
+}
+
+
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/dir1
+// BUILD: $CC foo.c -dynamiclib -install_name @rpath/libimplicitrpath.dylib -o $BUILD_DIR/dir1/libimplicitrpath.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dlopen-rpath-implicit.exe -rpath @loader_path/dir1
+
+// RUN: ./dlopen-rpath-implicit.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+/// test that a leaf name passed to dlopen() searches the rpath
+
+int main()
+{
+ printf("[BEGIN] dlopen-rpath-implicit\n");
+
+ void* handle = dlopen("libimplicitrpath.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("[FAIL] dlopen-rpath-implicit: dlopen(libimplicitrpath.dylib) failed: %s\n", dlerror());
+ return 0;
+ }
+
+ dlclose(handle);
+
+ printf("[PASS] dlopen-rpath-implicit\n");
+
+ return 0;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+
+__attribute__((constructor))
+void init()
+{
+ printf("[FAIL] dlopen-rpath-prev-override\n");
+ exit(0);
+}
--- /dev/null
+int sub2()
+{
+ return 2;
+}
+
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+int sub1()
+{
+ return 1;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/dir $BUILD_DIR/good $BUILD_DIR/bad
+// BUILD: $CC good.c -dynamiclib -install_name @rpath/libtest.dylib -o $BUILD_DIR/good/libtest.dylib
+// BUILD: $CC bad.c -dynamiclib -install_name @rpath/libtest.dylib -o $BUILD_DIR/bad/libtest.dylib
+// BUILD: $CC dyn.c -dynamiclib -install_name @rpath/libdynamic.dylib -o $BUILD_DIR/dir/libdynamic.dylib $BUILD_DIR/good/libtest.dylib -rpath @loader_path/../bad
+// BUILD: $CC main.c $BUILD_DIR/good/libtest.dylib -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-rpath-prev-override.exe -rpath @loader_path/good
+
+// RUN: ./dlopen-rpath-prev-override.exe
+
+// Processing of @rpath in dlopen()s always checks existing loaded images before using LC_RPATHs to find images on disk
+//
+// main links with @rpath/libtest.dylib found via -rpath @loader_path/good
+// main dlopen()s libdynamic.dylib which links with @rpath/libtest.dylib and has LR_PATH to find it in bad/,
+// but since it was already loaded from good/, that one should be re-used.
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+ printf("[BEGIN] dlopen-rpath-prev-override\n");
+
+ void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-rpath-prev-override\n");
+ return 0;
+ }
+
+ printf("[PASS] dlopen-rpath-prev-override\n");
+ return 0;
+}
+
--- /dev/null
+int foo()
+{
+ return 10;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/dir1 $BUILD_DIR/dir2
+// BUILD: $CC sub1.c -dynamiclib -install_name @rpath/librpathstatic.dylib -o $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libdynamic.dylib -o $BUILD_DIR/dir2/libdynamic.dylib $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libstatic.dylib -o $BUILD_DIR/libstatic.dylib -rpath @loader_path/dir1 $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD: $CC main.c $BUILD_DIR/libstatic.dylib -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-rpath-prev.exe
+
+// RUN: ./dlopen-rpath-prev.exe
+
+// main links with libstatic.dylib which uses rpath to link with dir1/librpathstatic.dylib
+// main dlopen()s libdynamic.dylib which links with dir1/librpathstatic.dylib, but has no rpath for it and depends on it being already loaded
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+ printf("[BEGIN] dlopen-rpath-prev\n");
+
+ void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ printf("dlerror(): %s\n", dlerror());
+ printf("[FAIL] dlopen-rpath-prev\n");
+ return 0;
+ }
+
+ printf("[PASS] dlopen-rpath-prev\n");
+ return 0;
+}
+
--- /dev/null
+int sub1()
+{
+ return 1;
+}
+
--- /dev/null
+int sub2()
+{
+ return 2;
+}
+
+// BUILD_ONLY: MacOSX
+
// 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
DYLD_TESTING_CALLBACK();
if (!DYLD_TESTING_CALLBACK_ENABLED())
- printf("[FAIL] !DYLD_TESTING_CALLBACK_ENABLED\n");
+ printf("[FAIL] dtrace: DYLD_TESTING_CALLBACK_ENABLED() returned false\n");
+ else
+ printf("[PASS] dtrace\n");
- printf("[PASS] dtrace\n");
return 0;
}
// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
// BUILD: $CC emptyMain.c $BUILD_DIR/libmissing.dylib -o $BUILD_DIR/prog_missing_dylib.exe
-// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
-// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
+// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
+// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
// BUILD: $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe
+// NO_CRASH_LOG: prog_missing_dylib.exe
+// NO_CRASH_LOG: prog_missing_symbol.exe
+
// RUN: ./dyld_abort_tests.exe
#include <stdio.h>
info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
info.eri_kcd_buf = (user_addr_t)packReasonData;
//fprintf(stderr, "info=%p\n", &info);
- if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
- printf("bad return size from proc_pidinfo()\n");
+ int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
+ if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
+ printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
return;
}
if ( info.eri_namespace != OS_REASON_DYLD ) {
- printf("eri_namespace != OS_REASON_DYLD\n");
+ printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
return;
}
if ( info.eri_code != sExpectedDyldReason ) {
- printf("eri_code != %lld\n", sExpectedDyldReason);
+ printf("eri_code (%llu) != %lld\n", sExpectedDyldReason, info.eri_code);
return;
}
kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size);
if ( sExpectedDylibPath != NULL ) {
if ( dyldInfo->targetDylibPathOffset != 0 ) {
const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
- if ( strcmp(sExpectedDylibPath, targetDylib) != 0 ) {
+ if ( strstr(targetDylib, sExpectedDylibPath) == NULL ) {
printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath);
return;
}
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/dyld_get_image_versions.exe
+
+// RUN: ./dyld_get_image_versions.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_image_versions\n");
+
+ // should succeed
+ dyld_get_image_versions(&__dso_handle, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
+ printf("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+ });
+
+ uint8_t badFile[4096];
+ struct mach_header* mh = (struct mach_header*)badFile;
+ mh->magic = MH_MAGIC_64;
+ mh->ncmds = 1;
+ mh->filetype = MH_DYLIB;
+ mh->sizeofcmds = 40;
+ struct load_command* lc = (struct load_command*)&badFile[32];
+ lc->cmd = 1;
+ lc->cmdsize = 4000; // bad load command size
+
+ // should detect the mh is bad and not crash or call the callback
+ dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
+ printf("bad binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+ });
+
+
+ printf("[PASS] dyld_get_image_versions\n");
+ return 0;
+}
+
return 0;
}
- printf("[PASS] dyld_get_sdk_version\n");
+#if TARGET_OS_WATCH
+ uint32_t iosVersion = dyld_get_program_sdk_version();
+ uint32_t watchOSVersion = dyld_get_program_sdk_watch_os_version();
+ if (iosVersion != (watchOSVersion + 0x00070000)) {
+ printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
+ return 0;
+ }
+#endif
+#if TARGET_OS_BRIDGE
+ uint32_t iosVersion = dyld_get_program_sdk_version();
+ uint32_t bridgeOSVersion = dyld_get_program_sdk_bridge_os_version();
+ if (bridgeOSVersion != (watchOSVersion + 0x00090000)) {
+ printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
+ return 0;
+ }
+#endif
+ printf("[PASS] dyld_get_sdk_version\n");
return 0;
}
#include <signal.h>
#include <spawn.h>
#include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
#include <mach/mach.h>
#include <mach/machine.h>
#include <mach-o/dyld_process_info.h>
bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started);
if ( valueSaysLaunchedSuspended != launchedSuspended ) {
printf("[FAIL] dyld_process_info suspend state mismatch\n");
+ _dyld_process_info_release(info);
return false;
}
__block bool foundDyld = false;
_dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
//fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
- if ( strstr(path, "/usr/lib/dyld") != NULL )
+ if ( strstr(path, "/dyld") != NULL )
foundDyld = true;
});
if ( strstr(path, "/linksWithCF.exe") != NULL )
foundMain = true;
});
+ _dyld_process_info_release(info);
return foundMain && foundDyld;
}
return foundCF && foundDyld;
}
+static void checkForLeaks(const char *name) {
+ printf("[BEGIN] %s checkForLeaks\n", name);
+ pid_t child;
+ int stat_loc;
+ char buffer[PAGE_SIZE];
+ (void)snprintf(&buffer[0], 128, "%d", getpid());
+
+ const char* argv[] = { "/usr/bin/leaks", buffer, NULL };
+ int psResult = posix_spawn(&child, "/usr/bin/leaks", NULL, NULL, (char**)argv, environ);
+ if ( psResult != 0 ) {
+ printf("[FAIL] %s checkForLeaks posix_spawn failed, err=%d\n", name, psResult);
+ exit(0);
+ }
+
+ (void)wait4(child, &stat_loc, 0, NULL);
+ ssize_t readBytes = 0;
+ if (WIFEXITED(stat_loc) == 0) {
+ printf("[FAIL] %s checkForLeaks leaks did not exit\n", name);
+ exit(0);
+ }
+ if (WEXITSTATUS(stat_loc) == 1) {
+ printf("[FAIL] %s checkForLeaks found leaks\n", name);
+ exit(0);
+ }
+ if (WEXITSTATUS(stat_loc) != 0) {
+ printf("[FAIL] %s checkForLeaks leaks errored out\n", name);
+ exit(0);
+ }
+ printf("[PASS] %s checkForLeaks\n", name);
+}
+
int main(int argc, const char* argv[])
{
}
printf("[PASS] dyld_process_info\n");
+ checkForLeaks("dyld_process_info");
+
return 0;
}
dispatch_semaphore_signal(taskDone);
}
}
- //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
- // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
- // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
},
^{
- //fprintf(stderr, "target exited\n");
gotTerminationNotice = true;
dispatch_semaphore_signal(taskDone);
},
{
dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dyld_process_info_notify handles[10];
+ // This loop goes through 10 iterations
+ // i = 0..7 Should succeed
+ // i = 8 Should fail, but trigger a release that frees up a slot
+ // i = 9 Should succeed
for (int i=0; i < 10; ++i) {
kern_return_t kr;
handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
dispatch_release(serviceQueue);
}
+static bool testSelfAttach(void) {
+ __block bool retval = false;
+ kern_return_t kr = KERN_SUCCESS;
+ dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+ dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), serviceQueue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ if ( strstr(path, "/libfoo.dylib") != NULL ) {
+ retval = true;
+ }
+ },
+ ^{},
+ &kr);
+ if ( handle == NULL ) {
+ fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d\n", kr);
+ }
+ void* h = dlopen("./libfoo.dylib", 0);
+ dlclose(h);
+ return retval;
+}
+
int main(int argc, const char* argv[])
{
- printf("[BEGIN] dyld_process_info_notify\n");
if ( argc < 2 ) {
printf("[FAIL] dyld_process_info_notify missing argument\n");
exit(0);
struct task_and_pid child;
// test 1) launch test program suspended in same arch as this program
+ printf("[BEGIN] dyld_process_info_notify laucnh suspended (same arch)\n");
child = launchTest(testProgPath, "", false, true);
if ( ! monitor(child, false, false) ) {
printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
exit(0);
}
killTest(child);
+ printf("[PASS] dyld_process_info_notify laucnh suspended (same arch)\n");
// test 2) launch test program in same arch as this program where it sleeps itself
+ printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
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");
- killTest(child);
+ killTest(child);
exit(0);
}
killTest(child);
+ printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
#if __MAC_OS_X_VERSION_MIN_REQUIRED
// test 3) launch test program suspended in opposite arch as this program
+ printf("[BEGIN] dyld_process_info_notify laucnh suspended (other arch)\n");
child = launchTest(testProgPath, "", true, true);
if ( ! monitor(child, false, false) ) {
printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
exit(0);
}
killTest(child);
+ printf("[PASS] dyld_process_info_notify laucnh suspended (other arch)\n");
// test 4) launch test program in opposite arch as this program where it sleeps itself
+ printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
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");
exit(0);
}
killTest(child);
+ printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
#endif
// test 5) launch test program where we disconnect from it after first dlopen
+ printf("[BEGIN] dyld_process_info_notify disconnect\n");
child = launchTest(testProgPath, "", false, true);
if ( ! monitor(child, true, false) ) {
printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
exit(0);
}
killTest(child);
+ printf("[PASS] dyld_process_info_notify disconnect\n");
+
+ // test 6) attempt to monitor the monitoring process
+ printf("[BEGIN] dyld_process_info_notify self-attach\n");
+ if (! testSelfAttach() ) {
+ printf("[FAIL] dyld_process_info_notify self notification\n");
+ }
+ printf("[PASS] dyld_process_info_notify self-attach\n");
- printf("[PASS] dyld_process_info_notify\n");
exit(0);
});
--- /dev/null
+
+// BUILD: $CC main.c -o $BUILD_DIR/versions.exe
+
+// RUN: ./versions.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+
+extern struct mach_header __dso_handle;
+
+int main()
+{
+ printf("[BEGIN] dyld_version_spi\n");
+ dyld_platform_t active = dyld_get_active_platform();
+ dyld_platform_t base = dyld_get_base_platform(active);
+ dyld_build_version_t absoluteMin = { .platform = base, .version = 0 };
+ dyld_build_version_t absoluteMax = { .platform = base, .version = 0xffffffff };
+
+#if TARGET_OS_OSX
+ if ( base != PLATFORM_MACOS ) {
+ printf("[FAIL] base.platform %u incorrect for macOS\n", base);
+ return 0;
+ }
+#elif TARGET_OS_IOS
+ if ( base != PLATFORM_IOS ) {
+ printf("[FAIL] base.platform %u incorrect for iOS\n", base);
+ return 0;
+ }
+#elif TARGET_OS_TV
+ if ( base != PLATFORM_TVOS ) {
+ printf("[FAIL] base.platform %u incorrect for tvOS\n", base);
+ return 0;
+ }
+#elif TARGET_OS_BRIDGE
+ if ( base != PLATFORM_BRIDGEOS ) {
+ printf("[FAIL] base.platform %u incorrect for wacthOS\n", base);
+ return 0;
+ }
+#elif TARGET_OS_WATCH
+ if ( base != PLATFORM_WATCHOS ) {
+ printf("[FAIL] base.platform %u incorrect for bridgeOS\n", base);
+ return 0;
+ }
+#else
+ printf("[FAIL] Running on unknown platform\n");
+ return 0;
+#endif
+
+#if TARGET_OS_SIMULATOR
+ if (dyld_is_simulator_platform(active) != true) {
+ printf("[FAIL] active platform %u should be a simulator\n", active);
+ return 0;
+ }
+#else
+ if (dyld_is_simulator_platform(active) == true) {
+ printf("[FAIL] active platform %u should not be a simulator\n", active);
+ return 0;
+ }
+#endif
+
+ if (dyld_is_simulator_platform(base) == true) {
+ printf("[FAIL] base platform %u should not be a simulator\n", base);
+ return 0;
+ }
+
+ if (!dyld_sdk_at_least(&__dso_handle, absoluteMin)) {
+ printf("[FAIL] executable sdk version should not < 1.0.0\n");
+ return 0;
+ }
+
+ if (dyld_sdk_at_least(&__dso_handle, absoluteMax)) {
+ printf("[FAIL] executable sdk version should not > 65536.0.0\n");
+ return 0;
+ }
+
+ if (!dyld_minos_at_least(&__dso_handle, absoluteMin)) {
+ printf("[FAIL] executable min version should not < 1.0.0\n");
+ return 0;
+ }
+
+ if (dyld_minos_at_least(&__dso_handle, absoluteMax)) {
+ printf("[FAIL] executable min version should not > 65536.0.0\n");
+ return 0;
+ }
+
+ printf("[PASS] dyld_version_spi\n");
+ return 0;
+}
+
// BUILD_ONLY: MacOSX
// BUILD_MIN_OS: 10.5
-// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
-// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -nostdlib -ldylib1.o
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -nostdlib -ldylib1.o
+// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR -nostdlib -lSystem -lcrt1.10.5.o
// RUN: ./dylib-re-export.exe
int main()
{
- printf("[BEGIN] dylib-re-export\n");
+ printf("[BEGIN] dylib-re-export-old-format\n");
if ( bar() == 42 )
- printf("[PASS] dylib-re-export\n");
+ printf("[PASS] dylib-re-export-old-format\n");
else
- printf("[FAIL] dylib-re-export, wrong value\n");
+ printf("[FAIL] dylib-re-export-old-format, wrong value\n");
return 0;
}
-// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe
// RUN: ./dylib-static-present.exe
-// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
// RUN: ./dylib-static-weak-present.exe
--- /dev/null
+int bar()
+{
+ return VALUE;
+}
+
--- /dev/null
+int foo()
+{
+ return VALUE;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/Bar.framework
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo_other.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar -install_name $RUN_DIR/Bar.framework/Bar -DVALUE=1
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar_alt -install_name $RUN_DIR/Bar.framework/Bar -DVALUE=42
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/Bar.framework/Bar
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+// BUILD: $CC main.c -o $BUILD_DIR/main-dynamic.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main-dynamic.exe
+
+// RUN: ./main.exe
+// RUN: DYLD_IMAGE_SUFFIX=_other ./main.exe
+// RUN: DYLD_IMAGE_SUFFIX=_alt ./main.exe
+// RUN: DYLD_IMAGE_SUFFIX=_alt:_other ./main.exe
+// RUN: ./main-dynamic.exe
+// RUN: DYLD_IMAGE_SUFFIX=_other ./main-dynamic.exe
+// RUN: DYLD_IMAGE_SUFFIX=_alt ./main-dynamic.exe
+// RUN: DYLD_IMAGE_SUFFIX=_alt:_other ./main-dynamic.exe
+
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+extern int foo();
+extern int bar();
+
+typedef int (*IntProc)();
+
+int main()
+{
+ const char* suffix = getenv("DYLD_IMAGE_SUFFIX");
+ if ( suffix == NULL )
+ suffix = "";
+ printf("[BEGIN] env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
+
+ const int expectedFoo = (strstr(suffix, "_other") != NULL) ? 42 : 1;
+ const int expectedBar = (strstr(suffix, "_alt") != NULL) ? 42 : 1;;
+
+#ifdef RUN_DIR
+ void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if ( fooHandle == NULL ) {
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, libfoo.dylib could not be loaded, %s\n", suffix, dlerror());
+ return 0;
+ }
+ void* barHandle = dlopen(RUN_DIR "/Bar.framework/Bar", RTLD_LAZY);
+ if ( barHandle == NULL ) {
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, Bar.framework/Bar could not be loaded, %s\n", suffix, dlerror());
+ return 0;
+ }
+ IntProc fooProc = (IntProc)dlsym(fooHandle, "foo");
+ if ( fooProc == NULL ) {
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, symbol 'foo' not found %s\n", suffix, dlerror());
+ return 0;
+ }
+ IntProc barProc = (IntProc)dlsym(barHandle, "bar");
+ if ( barProc == NULL ) {
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, symbol 'bar' not found %s\n", suffix, dlerror());
+ return 0;
+ }
+ int fooValue = (*fooProc)();
+ int barValue = (*barProc)();
+#else
+ int fooValue = foo();
+ int barValue = bar();
+#endif
+ if ( fooValue != expectedFoo )
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, foo()=%d expected=%d\n", suffix, fooValue, expectedFoo);
+ else if ( barValue != expectedBar )
+ printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, bar()=%d expected=%d\n", suffix, barValue, expectedBar);
+ else
+ printf("[PASS] env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
+
+ return 0;
+}
+
--- /dev/null
+
+// BUILD: mkdir -p $BUILD_DIR/override
+// BUILD: $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation
+// BUILD: $CC main.c -o $BUILD_DIR/main.exe -lz
+// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN: ./main.exe
+// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/override/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <stdbool.h>
+
+// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation.
+
+int main()
+{
+ bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL);
+
+ printf("[BEGIN] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+
+ bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
+
+ if ( usingMyDylib == expectMyDylib )
+ printf("[PASS] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+ else
+ printf("[FAIL] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+
+ return 0;
+}
+
--- /dev/null
+const char* zlibVersion()
+{
+ return "my";
+}
#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
+ // bump ptr allocate twice the size and fills with '#'
+ char* result = malloc(size*2);
+ memset(result, '#', size*2);
return result;
}
void myfree(void* p)
{
+ free(p);
}
DYLD_INTERPOSE(mymalloc, malloc)
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CC main.c -framework AppKit -o $BUILD_DIR/no_shared_cache.exe
+
+// RUN: DYLD_SHARED_REGION=avoid ./no_shared_cache.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+
+// This program links with AppKit which in dyld3 mode stress tests building closures when there is no dyld shared cache
+
+int main()
+{
+ printf("[BEGIN] no-shared-cache\n");
+
+ size_t cacheLen;
+ const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
+
+ if ( cacheStart != NULL ) {
+ printf("[FAIL] no-shared-cache: _dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache\n", cacheStart);
+ return 0;
+ }
+
+ printf("[PASS] no-shared-cache\n");
+ return 0;
+}
+
// will turn around and call operator new in this main exectuable
//
-static void* ptr;
+static void* myLastNewAllocation;
+static void* myLastDelete;
+// Note: this is not weak. That is specifically suppported
void* operator new(size_t s) throw (std::bad_alloc)
{
- ptr = malloc(s);
- return ptr;
+ myLastNewAllocation = malloc(s);
+ return myLastNewAllocation;
+}
+
+struct Foo {
+ int bytes[10];
+};
+
+// Note: this is weak and because it is in main executable should override OS
+__attribute__((weak))
+void operator delete(void* p) throw()
+{
+ myLastDelete = p;
+ ::free(p);
}
int main()
{
printf("[BEGIN] operator-new\n");
+ // test that OS's operator new[] redirects to my operator new
+ myLastNewAllocation = NULL;
char* stuff = new char[24];
- if ( (void*)stuff == ptr )
- printf("[PASS] operator-new\n");
- else
- printf("[FAIL] operator-new\n");
+ if ( (void*)stuff != myLastNewAllocation ) {
+ printf("[FAIL] operator-new system array allocator not redirected through my operator new\n");
+ return 0;
+ }
+
+ // test that program uses my operator new
+ myLastNewAllocation = NULL;
+ Foo* foo = new Foo();
+ if ( (void*)foo != myLastNewAllocation ) {
+ printf("[FAIL] operator-new allocation not redirected though my operator new\n");
+ return 0;
+ }
+
+ //
+ delete foo;
+ if ( (void*)foo != myLastDelete ) {
+ printf("[FAIL] operator-new deallocation not redirected though my operator delete\n");
+ return 0;
+ }
+ printf("[PASS] operator-new\n");
return 0;
}
#include <mach-o/dyld_priv.h>
#include <dlfcn.h>
+#if __has_feature(ptrauth_calls)
+ #include <ptrauth.h>
+#endif
+
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+ return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+ return ptr;
+#endif
+}
+
int main()
{
const void* cacheEnd = (char*)cacheStart + cacheLen;
// verify malloc is in shared cache
- if ( ((void*)&malloc < cacheStart) || ((void*)&malloc > cacheEnd) ) {
+ if ( (stripPointer((void*)&malloc) < cacheStart) || (stripPointer((void*)&malloc) > cacheEnd) ) {
printf("[FAIL] shared_cache_range: malloc is outside range of cache\n");
return 0;
}
--- /dev/null
+#include <stdlib.h>
+
+
+static int foo_ten()
+{
+ return 10;
+}
+
+static int foo_zero()
+{
+ return 0;
+}
+
+
+// This foo is a "resolver" function that return the actual address of "foo"
+void* foo()
+{
+ __asm__(".symbol_resolver _foo"); // magic until we have compiler support
+ if ( getenv("TEN") != NULL )
+ return &foo_ten;
+ else
+ return &foo_zero;
+}
+
+
--- /dev/null
+
+#include <stdlib.h>
+
+extern int foo();
+
+// test that calls to resolver based function in same dylib work
+int fooPlusOne()
+{
+ return foo() + 1;
+}
+
--- /dev/null
+
+// BUILD: $CC foo.c foo2.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/symbol-resolver.exe
+
+// RUN: ./symbol-resolver.exe
+// RUN: TEN=1 ./symbol-resolver.exe
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+extern int fooPlusOne();
+
+
+int main()
+{
+ if ( getenv("TEN") != NULL ) {
+ if ( foo() != 10 )
+ printf("[FAIL] symbol-resolver-basic: foo() != 10\n");
+ else if ( fooPlusOne() != 11 )
+ printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 11\n");
+ else
+ printf("[PASS] symbol-resolver-basic\n");
+ }
+ else {
+ if ( foo() != 0 )
+ printf("[FAIL] symbol-resolver-basic: foo() != 0\n");
+ else if ( fooPlusOne() != 1 )
+ printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 1\n");
+ else
+ printf("[PASS] symbol-resolver-basic\n");
+ }
+
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+main: main.c libfoo1.dylib libbase.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib
+
+libfoo1.dylib: foo1.c libfoo2.dylib libbase.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib
+
+libfoo2.dylib: foo2.c libfoo3.dylib libbase.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib
+
+libfoo3.dylib: foo3.c libbase.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000
+
+libbase.dylib: base.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000
+
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libfoo3.dylib libbase.dylib
+
--- /dev/null
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "base.h"
+
+static bool wasProblem = false;
+static const char* coal1Where = NULL;
+static int* coal1Addr = NULL;
+static int checkInCountCoal1 = 0;
+
+void baseVerifyCoal1(const char* where, int* addr)
+{
+ //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr);
+ ++checkInCountCoal1;
+ if ( coal1Where == NULL ) {
+ coal1Where = where;
+ coal1Addr = addr;
+ }
+ else {
+ if ( addr != coal1Addr ) {
+ fprintf(stderr, "coal1 resolved to different locations. %p in %s and %p in %s\n",
+ coal1Addr, coal1Where, addr, where);
+ wasProblem = true;
+ }
+ }
+}
+
+
+static const char* coal2Where = NULL;
+static int* coal2Addr = NULL;
+static int checkInCountCoal2 = 0;
+
+void baseVerifyCoal2(const char* where, int* addr)
+{
+ //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr);
+ ++checkInCountCoal2;
+ if ( coal2Where == NULL ) {
+ coal2Where = where;
+ coal2Addr = addr;
+ }
+ else {
+ if ( addr != coal2Addr ) {
+ fprintf(stderr, "coal2 resolved to different locations. %p in %s and %p in %s\n",
+ coal2Addr, coal2Where, addr, where);
+ wasProblem = true;
+ }
+ }
+}
+
+
+
+void baseCheck()
+{
+ if ( wasProblem )
+ printf("[FAIL] weak-coalesce: was problem\n");
+ else if ( checkInCountCoal1 != 4 )
+ printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n");
+ else if ( checkInCountCoal2 != 4 )
+ printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n");
+ else
+ printf("[PASS] weak-coalesce\n");
+}
+
--- /dev/null
+
+
+extern void baseCheck();
+
+extern int coal1;
+extern int coal2;
+
+extern void baseVerifyCoal1(const char* where, int* addr);
+extern void baseVerifyCoal2(const char* where, int* addr);
--- /dev/null
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include "base.h"
+
+
+
+int __attribute__((weak)) coal1 = 1;
+int __attribute__((weak)) coal2 = 1;
+
+
+static __attribute__((constructor)) void myinit()
+{
+ //fprintf(stderr, "myinit() in foo1.c\n");
+ baseVerifyCoal1("in foo1", &coal1);
+ baseVerifyCoal2("in foo1", &coal2);
+}
+
+
--- /dev/null
+#include <stdio.h>
+
+#include "base.h"
+
+int coal1 = 2; // note: this is not weak and therefore should win
+int __attribute__((weak)) coal2 = 2;
+
+static __attribute__((constructor))
+void myinit()
+{
+ //fprintf(stderr, "myinit() in foo1.c\n");
+ baseVerifyCoal1("in foo2", &coal1);
+ baseVerifyCoal2("in foo2", &coal2);
+}
--- /dev/null
+#include <stdio.h>
+
+#include "base.h"
+
+int __attribute__((weak)) coal1 = 3;
+int __attribute__((weak)) coal2 = 2;
+
+static __attribute__((constructor))
+void myinit()
+{
+ //fprintf(stderr, "myinit() in foo1.c\n");
+ baseVerifyCoal1("in foo3", &coal1);
+ baseVerifyCoal2("in foo3", &coal2);
+}
+
--- /dev/null
+
+// BUILD: $CC base.c -dynamiclib -install_name $RUN_DIR/libbase.dylib -o $BUILD_DIR/libbase.dylib
+// BUILD: $CC foo3.c -dynamiclib -install_name $RUN_DIR/libfoo3.dylib -o $BUILD_DIR/libfoo3.dylib $BUILD_DIR/libbase.dylib
+// BUILD: $CC foo2.c -dynamiclib -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib $BUILD_DIR/libfoo3.dylib $BUILD_DIR/libbase.dylib
+// BUILD: $CC foo1.c -dynamiclib -install_name $RUN_DIR/libfoo1.dylib -o $BUILD_DIR/libfoo1.dylib $BUILD_DIR/libfoo2.dylib $BUILD_DIR/libbase.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo1.dylib $BUILD_DIR/libbase.dylib -o $BUILD_DIR/weak-coalesce.exe
+
+// RUN: ./weak-coalesce.exe
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "base.h"
+
+int main()
+{
+ printf("[BEGIN] weak-coalesce\n");
+
+ baseVerifyCoal1("in main", &coal1);
+ baseVerifyCoal2("in main", &coal2);
+
+ baseCheck();
+ return 0;
+}
+